-1

我在 Mac OS X 中从服务器到客户端 (C++) 接收文件名时遇到问题。我发送了一个序列化对象,它有一个带有文件名的字符指针,有时是一个字符串对象。当我在客户端收到它时,它似乎有字符 %F6 或 %E9。这个问题在 Windows 操作系统中不会出现,即使它是相同的代码。有没有办法将这些 '%' 字符解码回它们在 Mac OS 和 Linux 中的原始形式?

我遇到了一些问题:

ǡ ȅ ȉ

更改服务器中的代码会很困难,因此如果有办法将字符解码回其原始形式,那就更容易了。

4

1 回答 1

2

看起来这些字符正在使用 ISO 8859-1 或一些类似的单字节代码集的假设进行 URL 编码。简单的答案是您需要转换"%F6"'\xF6'; 也就是说,您需要将百分比加上两个十六进制数字转换为相应的单字节。

这会给你在 Mac OS X 上留下一个问题,因为文件名通常存储在 UTF-8 中,而不是 ISO 8859-1 等中。例如(我的提示是'Osiris JL:'):

Osiris JL: mkdir x
Osiris JL: cd x
Osiris JL: cp /dev/null é
Osiris JL: cp /dev/null è
Osiris JL: ls | odx
0x0000: 65 CC 80 0A 65 CC 81 0A                           e...e...
0x0008:
Osiris JL: ls
è  é
Osiris JL: ls | cat
è
é
Osiris JL: ls | utf8-unicode
(standard input):
0x65 = U+0065
0xCC 0x80 = U+0300
0x0A = U+000A
0x65 = U+0065
0xCC 0x81 = U+0301
0x0A = U+000A
Osiris JL: 

Unicode 字符是 U+0065 LATIN SMALL LETTER E 加上 U+0300 COMBINING GRAVE ACCENT 或 U+0301 COMBINING ACUTE ACCENT。

这不是字母 é 和 è 的通常格式;它们通常被视为带有 ACUTE 的 U+00E9 拉丁小写字母 E 和带有 GRAVE 的 U+00E8 拉丁小写字母 E。

请注意,这\xF6在 UTF-8 文本中根本不是有效字节,但在 ISO 8859-1、ISO 8859-15(和 Windows CP1252)中,0xF6 是 ö,U+00F6 带有分音符号的拉丁小写字母 O。

Mac OS X 上的示例文件创建

这是一个创建一些文件的程序——源文件x.c,在 Mac OS X 10.7.5 上运行,使用 GCC 4.7.1 编译:

#include <dirent.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

static void create_file(const char *name)
{
    int fd = open(name, O_CREAT|O_TRUNC|O_RDWR, 0644);
    if (fd >= 0)
    {
        close(fd);
        printf("Created file %s OK\n", name);
    }
    else
    {
        printf("Failed to create file %s\n", name);
    }
}

static void print_name(const char *name)
{
    size_t len = strlen(name);
    printf("%-10s = ", name);
    for (size_t i = 0; i < len; i++)
        printf(" %.2X", (unsigned char)name[i]);
    putchar('\n');
}

int main(void)
{
    const char *names[] =
    {
        "a-e\xCC\x80",  /* a-e\u0300 */
        "a-e\xCC\x81",  /* a-e\u0301 */
        "b-\xC3\xA8",   /* b-\u00E8  */
        "b-\xC3\xA9",   /* b-\u00E9  */
        "c-\xF6",
        "c-\xE9",
    };
    enum { NUM_NAMES = sizeof(names) / sizeof(names[0]) };

    for (int i = 0; i < NUM_NAMES; i++)
        create_file(names[i]);

    DIR *dp = opendir(".");
    if (dp != 0)
    {
        struct dirent *entry;
        while ((entry = readdir(dp)) != 0)
            print_name(entry->d_name);
        closedir(dp);
    }
    else
        fprintf(stderr, "error: failed to open current directory\n");

    return(0);
}

这将两种编码用于带有尖音或重音的拉丁小写字母“e”。

它运行干净,但您可以看到文件名被规范化以使用组合重音符号,即使在文件名字符串中使用 U+00E8 或 U+00E9 指定:

Osiris JL: ls
è       é       makefile x        x.c
Osiris JL: ./x
Created file a-è OK
Created file a-é OK
Created file b-è OK
Created file b-é OK
Created file c-? OK
Created file c-? OK
.          =  2E
..         =  2E 2E
a-è      =  61 2D 65 CC 80
a-é      =  61 2D 65 CC 81
b-è      =  62 2D 65 CC 80
b-é      =  62 2D 65 CC 81
c-%E9      =  63 2D 25 45 39
c-%F6      =  63 2D 25 46 36
è        =  65 CC 80
é        =  65 CC 81
makefile   =  6D 61 6B 65 66 69 6C 65
x          =  78
x.c        =  78 2E 63
Osiris JL: ls
a-è     a-é     b-è     b-é     c-%E9    c-%F6    è       é       makefile x        x.c
Osiris JL: ls | utf8-unicode
(standard input):
0x61 = U+0061
0x2D = U+002D
0x65 = U+0065
0xCC 0x80 = U+0300
0x0A = U+000A
0x61 = U+0061
0x2D = U+002D
0x65 = U+0065
0xCC 0x81 = U+0301
0x0A = U+000A
0x62 = U+0062
0x2D = U+002D
0x65 = U+0065
0xCC 0x80 = U+0300
0x0A = U+000A
0x62 = U+0062
0x2D = U+002D
0x65 = U+0065
0xCC 0x81 = U+0301
0x0A = U+000A
0x63 = U+0063
0x2D = U+002D
0x25 = U+0025
0x45 = U+0045
0x39 = U+0039
0x0A = U+000A
0x63 = U+0063
0x2D = U+002D
0x25 = U+0025
0x46 = U+0046
0x36 = U+0036
0x0A = U+000A
0x65 = U+0065
0xCC 0x80 = U+0300
0x0A = U+000A
0x65 = U+0065
0xCC 0x81 = U+0301
0x0A = U+000A
0x6D = U+006D
0x61 = U+0061
0x6B = U+006B
0x65 = U+0065
0x66 = U+0066
0x69 = U+0069
0x6C = U+006C
0x65 = U+0065
0x0A = U+000A
0x78 = U+0078
0x0A = U+000A
0x78 = U+0078
0x2E = U+002E
0x63 = U+0063
0x0A = U+000A
Osiris JL: 

这意味着在创建包含拉丁小写字母 E WITH GRAVE 的文件的程序中,字符 è 有两种可能的拼写。

关于程序的输出有很多有趣的观察,例如=符号的错位。但是一个关键点是,如果你创建的文件名中包含一个无效的 UTF-8 字符序列,每个无效字节都会被 URL 编码为无效字节对应的十六进制值(在磁盘上占用 3 个字节,而%xx不是xx1,AFAICT)。

概括

你必须决定你的源字符集是什么,这样你才能在 Mac OS X 上准确地将字节从 0x80..0xFF 转换为适当的 Unicode 字符,或者你将不得不容忍 Mac OS X 为你创建文件名%F6代替ö等。文件系统将为您规范化文件名,但您必须为其提供有效的 UTF-8 名称。

于 2013-01-06T21:57:12.327 回答