我在Mac OS X中从服务器到客户端(C ++)接收文件名时遇到问题。我发送了一个序列化对象,它有一个带有文件名的char指针或有时是一个字符串对象。当我在客户端收到它时,它似乎有%F6或%E9字符。尽管Windows操作系统中的代码相同,但在Windows操作系统中不会出现此问题。是否有任何方法可以将这些'%'字符解码回Mac OS&amp ;; Linux呢?
我遇到的一些问题:
ǡȅȉ
要更改服务器中的代码是很困难的,所以如果有办法将字符解码回原始形式,那就更容易了。
答案 0 :(得分: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组合ACUTE ACCENT。
这不是字母é和è的通常形式;它们通常被视为U + 00E9拉丁文小写字母E,带有急性和U + 00E8拉丁文小写字母E和GRAVE。
请注意\xF6
根本不是UTF-8文本中的有效字节,但在ISO 8859-1,ISO 8859-15(和Windows CP1252)中,0xF6是ö,U + 00F6 LATIN SMALL LETTER O与DIAERESIS。
这是一个创建一些文件的程序 - 源文件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:
这意味着程序中的角色è有两种可能的拼写形式,用于创建包含LATIN SMALL LETTER E WITH GRAVE的文件。
关于程序的输出,有许多有趣的观察结果,例如=
符号的错位。但关键的一点是,如果您在名称中创建的UTF-8字符序列无效的文件名,则每个无效字节都会对%xx
进行URL编码,其中xx
是与无效字符对应的十六进制值byte(在磁盘上占用3个字节,而不是1,AFAICT)。
您必须确定源字符集是什么,以便您可以准确地将0x80..0xFF范围内的字节转换为Mac OS X上的相应Unicode字符,否则您将不得不容忍Mac OS X创建文件名为您%F6
代替ö
等。文件系统会为您标准化文件名,但您必须为其提供有效的UTF-8名称。