这是我的旧闻,发布在spaces.live.com上。
这是一个相当繁琐的主题,起因是我在做TCPL上的作业时,想到用普通的char来储存stdin的中文输入会怎么样,结果发现buf分两次把值交付给getchar()。于是我写了一个小程序:
int ch = '/0';
while (EOF != ch) {
int i = 16;
while (i-- > 0) {
ch = getchar();
if (EOF == ch)
break;
printf("0x%02X ", ch);
}
putchar('/n');
i = 16;
}
printf("EOF");
当然这个不是第一版,但是有用。它用来打印每个文件的内容的机器表示。
然 后有一下几个文件en,du,dt,db,da分别表示英文“ab”,unicode的,utf-8的,unicode big-endian的和ansi的‘茅’,用的是unix的文件换行格式(这个其实无所谓,我没有换行),另外,我又增补了du2、db2和dt2,内 容为“盛”,dt3 “在”,以下为Makefile:
dummy:
wc -cm en > res
./a < en >> res
./a < du >> res
./a < du2 >> res
wc -cm db >> res
./a < db >> res
wc -cm db2 >> res
./a < db2 >> res
wc -cm dt >> res
./a < dt >> res
wc -cm da >> res
./a < da >> res
结果为:(res)
2 2 en
0x61 0x62
EOF
# wc 对 du 会报错
0xFF 0xFE 0x05 0x83
EOF
# 这里是du2
0xFF 0xFE 0xDB 0x76
EOF
2 4 db
0xFE 0xFF 0x83 0x05
EOF
2 4 db2
0xFE 0xFF 0x76 0xDB
EOF
5 8 dt
0xEF 0xBB 0xBF 0xE8 0x8C 0x85 0x0A
EOF
3 6 dt2
0xEF 0xBB 0xBF 0xE7 0x9B 0x9B
EOF
1 2 da
0xC3 0xA9
EOF
奇怪的结果。
1. 首先可以发现IA32是Little-Endian的,所以du和db的结果是两位相反,表示汉字作为一个单位,是unicode-16格式的,在单位内决定顺序。
2. 显然0xFE 0xFF是unicode的header,而utf-8的是0xEF 0xBB 0xBF,为什么会又0xE8 0xE7两个不同的第三位是说不过去的。首先猜测,由于utf-8是为了适应unix这种完全构建在anscii上的操作系统实现unicode的折中方 案,所以它是一个一个一个读入数据的,并且认为每两个字节之间是没有太大关系的,所以即使头是三个字节也是无所谓的。但是我还是倾向于认为头四个都是 header。(这个结论呆会就
会被推翻的)
3. wc对big-endian支持正常,而little-endian就异常了。
4. 观察wc的返回值,就可以发现bytes和char数目的差距,就是header的长度,于是,utf-8是三位长的header!!!
5. 又一个诡异的值:一般用记事本写完之后直接保存将会得到一个ansi的文件(我以前的机器是),可以正常打开的,而这次不同,会识别为可怕的法国字 母/'{e}(这个是TeX的表示法,就是拼音中第二声的e),而用vim打开是正常的。这个似乎是GB2312的编码(不确定)。
P.S. 这部分关于unix和w32的换行符。在w32中,你用'/n'得到的是0x0A,就是“Line Feed”,而存储后会变成两个字符。这个用stdin和stdout无法检测出的,我的两个文件a.txt b.txt都是一个回车,不过一个是w32,另一个是unix,用上面的程序借个都是0x0A。但是你用记事本看过README之类的文件就知道差别了。
这是一个相当繁琐的主题,起因是我在做TCPL上的作业时,想到用普通的char来储存stdin的中文输入会怎么样,结果发现buf分两次把值交付给getchar()。于是我写了一个小程序:
int ch = '/0';
while (EOF != ch) {
int i = 16;
while (i-- > 0) {
ch = getchar();
if (EOF == ch)
break;
printf("0x%02X ", ch);
}
putchar('/n');
i = 16;
}
printf("EOF");
当然这个不是第一版,但是有用。它用来打印每个文件的内容的机器表示。
然 后有一下几个文件en,du,dt,db,da分别表示英文“ab”,unicode的,utf-8的,unicode big-endian的和ansi的‘茅’,用的是unix的文件换行格式(这个其实无所谓,我没有换行),另外,我又增补了du2、db2和dt2,内 容为“盛”,dt3 “在”,以下为Makefile:
dummy:
wc -cm en > res
./a < en >> res
./a < du >> res
./a < du2 >> res
wc -cm db >> res
./a < db >> res
wc -cm db2 >> res
./a < db2 >> res
wc -cm dt >> res
./a < dt >> res
wc -cm da >> res
./a < da >> res
结果为:(res)
2 2 en
0x61 0x62
EOF
# wc 对 du 会报错
0xFF 0xFE 0x05 0x83
EOF
# 这里是du2
0xFF 0xFE 0xDB 0x76
EOF
2 4 db
0xFE 0xFF 0x83 0x05
EOF
2 4 db2
0xFE 0xFF 0x76 0xDB
EOF
5 8 dt
0xEF 0xBB 0xBF 0xE8 0x8C 0x85 0x0A
EOF
3 6 dt2
0xEF 0xBB 0xBF 0xE7 0x9B 0x9B
EOF
1 2 da
0xC3 0xA9
EOF
奇怪的结果。
1. 首先可以发现IA32是Little-Endian的,所以du和db的结果是两位相反,表示汉字作为一个单位,是unicode-16格式的,在单位内决定顺序。
2. 显然0xFE 0xFF是unicode的header,而utf-8的是0xEF 0xBB 0xBF,为什么会又0xE8 0xE7两个不同的第三位是说不过去的。首先猜测,由于utf-8是为了适应unix这种完全构建在anscii上的操作系统实现unicode的折中方 案,所以它是一个一个一个读入数据的,并且认为每两个字节之间是没有太大关系的,所以即使头是三个字节也是无所谓的。但是我还是倾向于认为头四个都是 header。(这个结论呆会就
会被推翻的)
3. wc对big-endian支持正常,而little-endian就异常了。
4. 观察wc的返回值,就可以发现bytes和char数目的差距,就是header的长度,于是,utf-8是三位长的header!!!
5. 又一个诡异的值:一般用记事本写完之后直接保存将会得到一个ansi的文件(我以前的机器是),可以正常打开的,而这次不同,会识别为可怕的法国字 母/'{e}(这个是TeX的表示法,就是拼音中第二声的e),而用vim打开是正常的。这个似乎是GB2312的编码(不确定)。
P.S. 这部分关于unix和w32的换行符。在w32中,你用'/n'得到的是0x0A,就是“Line Feed”,而存储后会变成两个字符。这个用stdin和stdout无法检测出的,我的两个文件a.txt b.txt都是一个回车,不过一个是w32,另一个是unix,用上面的程序借个都是0x0A。但是你用记事本看过README之类的文件就知道差别了。