|
3.1 读取ASCII编码
选择ASCII编码保存文件,文件内容是:您好, Ye Ming!
FILE *myfile=fopen("CharsetExample.txt","rb");
char a[100];
fgets(a,100,myfile);
printf("字节数=%d\n",strlen(a));
printf("16进制值=");
for(int i=0;i<strlen(a);i++)
printf("%hhx\t",a[i]);
printf("\n字符串输出=%s\n",a);
结果输出:
字节数=13
16进制值=c4 fa ba c3 2c 59 65 20 4d 69 6e 67 21
字符串输出=您好,Ye Ming!
您=c4fa, 好=bac3,其它只占一个字节。printf函数发现字节的第1位是1时,会继续联合下一个字节按照默认的ASCII编码如GB2312显示汉字。
为了测试一下系统能否显示繁体中文字,将文本更改为:案與產品
保存为ASCII编码,然后运行程序。结果可以显示出来,说明系统用GBK或BIG5解码了。在控制面板把BIG5去掉,还能正常显示,说明系统默认的是GBK解码。
注解1:文本是按字节读,二进制是按逻辑单位读;这个例子用二进制读和用文本读的结果是一样的,因为逻辑单位char是1个字节的。
3.2 读取UTF-8编码
选择UTF-8编码保存文件,文件内容是:您好, Ye Ming!
FILE *myfile=fopen("CharsetExample.txt","rb");
char a[100];
fgets(a,100,myfile);
printf("字节数=%d\n",strlen(a));
printf("16进制值=");
for(int i=0;i<strlen(a);i++)
printf("%hhx\t",a[i]);
printf("\n字符串输出=%s\n",&a[3]);
结果输出:
字节数=18
16进制值=ef bb bf e6 82 a8 e5 a5 bd 2c 59 65 20 4d 69 6e 67 21
字符串输出=乱码,Beijing!
其中,ef bb bf 是UTF-8的头标识;您=e6 82 a8,好=e5 a5 bd。
其他是单字节的ASCII码。
输出字符串时要去掉前三个字节。但是,还是出现了乱码!
之所以出现乱码,是因为系统按默认的GB2312去解UTF-8的码,所以在库里找不到或找不对对应的汉字,所以出现3个乱码,因为按两两组合所以有3个乱码。
3.3 读取UTF-16 Big Endian
选择UTF-8编码保存文件,文件内容是:您好, Ye Ming!
C++程序读取并显示如下:
FILE *myfile=fopen("CharsetExample.txt","rb");
char a[100];
fgets(a,100,myfile);
printf("字节数=%d\n",strlen(a));
printf("16进制值=");
for(int i=0;i<strlen(a);i++)
printf("%hhx\t",a[i]);
printf("\n字符串输出=%s\n",&a[2]);
结果输出:
字节数=6
16进制值=fe ff 60 a8 59 7d
字符串输出=三个符号
其中,fe ff 是UTF-16 Big Endian的头标识;60是可以输出的,a859输出乱码,7d是可以输出的。输出字符串时要去掉前两个字节。
理论上字节数应该是2+11*2=24。可是,输出为什么只有6呢?在UTF-16中,每个字符都用两个字节表示,而ASCII符号只用一个字节,高位用0表示,所以,当遇到0后,strlen认为串结束了。这就是UTF-16的缺点,前面提到过。
将程序的一句更改后:
for(int i=0;i<24;i++)
16进制值= fe ff 60 a8 59 7d 0 2c 0 59……
同样,字符串仍然无法显示。
3.4 读取UTF-16 Little Endian
文本文件用UTF-16 Little Endian编码时,其输出结果是:
16进制值= ff fe a8 60 7d 59 2c 0 59 0 ……
4. 编程:各字符集编码的显示
4.1 直接显示
由于中文系统默认的编码是中文编码,一般是GBK。所以,ASCII编码的显示没有问题。但是,遇到UNICODE编码就会有问题了,如上面的例子,显示会出现乱码。
接下来本文介绍一种读取UNICODE文件并正确显示中文的方法。
4.1.1 文件编码为UNICODE Big Endian
本件保存为UNICODE Big Endian,文本=您好, Ye Ming!
· 错误:按文本方式读
读文本程序如下:
FILE *myfile;
wchar_t *name=L"CharsetExample.txt";
myfile=_wfopen(name,L"rt");
fseek(myfile,0,0);
wchar_t buffw[100];
fgetws(buffw,100,myfile);
printf("字节数=%d\n",wcslen(buffw));
printf("16进制值=");
for(int i=0;i<wcslen(buffw);i++)
printf("%hhx\t",buffw[i]);
//设置本地化信息,.936是简体中文的Code Page。wcout需要依靠它决定将宽字符按什么编码显示
setlocale(LC_ALL, ".936");
printf("\n字符串输出=");
wcout<<buffw+1<<endl;
结果如下:
字节数=6
16进制值=fe ff 60 a8 59 7d
字符串输出=’”Y}
文本是按UNICODE编码的,每个字符是两字节,ASCII字符高位用0代替。按文本方式打开文件读时,是按字节的读写,一个字节就存进了两个字节大小的结构wchar_t中。遇到0时,它认为文件已经结束,停止操作。
· 正确:按二进制方式读
读文本程序如下:
将myfile=_wfopen(name,L"rt")改为myfile=_wfopen(name,L"rb");
结果如下:
字节数=12
16进制值=fffe a860 7d59 2c00 5900….
字符串输出=乱码
这一次二进制结果是对的,但是,为什么还是乱码呢?问题出在wcout函数上,x86是按照Little Endian字节序处理数据的,它将2c00处理了而不是002c。我们看一下如果文本保存为UNICODE Little Endian, 会怎么样?
4.1.2 文件编码为UNICODE Little Endian
本件保存为UNICODE Little Endian,文本=您好, Ye Ming!
· 错误:按文本方式读
基本同上,故略。
· 正确:按二进制方式读
将myfile=_wfopen(name,L"rt")改为myfile=_wfopen(name,L"rb");
结果如下:
字节数=12
16进制值=feff 60a8 597d 2c 59….
字符串输出=您好, Ye Ming!
输出正确,^_^。
|