|
char[]和wchar_t[]的根本区别 |
一派护法 十九级 |
char[]是多字节字符串,一个字符可能用一个数组元素表示,也有可能用多个数组元素表示。 而wchar_t[]每个字符固定使用一个数组元素表示。
因此,char和wchar_t的根本区别是字符串的存储方式,而不是字符串的编码。从理论上来说,它们都可以用来存储任意编码的任意字符串。不过实际上,因为在不同编译器上定义wchar_t的位数不同,可能是16或32位。而UTF8中汉字是用3个字节存储的,所以,当编译器把wchar_t认作是16位时,wchar_t字符数组不能用来存放UTF8编码的字符串。
|
一派护法 十九级 |
基于这个原因,一般我们要存储任意编码的字符串时,特别是在进行编码转换时,都是用char[]字符数组。不过用char[]的话,计算字符串中的字符个数会比较麻烦,在字符串中移动光标也更麻烦。
|
一派护法 十九级 |
在Windows下的VC++编译器下,字符串(无论是单引号还是双引号括起来的)的默认编码是ANSI,这种编码与系统locale高度相关。比如说,在中文Win7下用记事本写一个含有汉字的文本文件,再在英文版Win7下打开,就会发现文件中凡是中文的地方都全部乱码了,而英文字母正常。此时,如果把英文版Win7的控制面板中的locale改成Simplified Chinese PRC的话,那么再打开txt文件,中文就能正常显示了。
而Linux下的gcc,g++编译器下,字符串的默认编码就是UTF-8,一个汉字占三个字节。输出的txt文件如果含有中文,那么无论是用什么语言的Windows系统打开,只要安装了中文字体,无需改动locale设置就能正常显示中文字符。
对于如下C++代码: char str[] = "中文abc"; cout << sizeof(str) / sizeof(char) << endl; 在Windows下的VC++中编译,输出的是8。 而在Linux下的g++中编译,输出的却是10。 这一点,充分说明了char字符数组可以存储任意编码的字符串。
|
一派护法 十九级 |
对于语句char str[] = "中文abc"; 在VC++下,形成的字符数组是: {'中' >> 8, '中' & 0xff, '文' >> 8, '文' & 0xff, 'a', 'b', 'c', '\0'} 而在g++下形成的却是: {'中' >> 16, '中' >> 8 & 0xff, '中' & 0xff, '文' >> 16, '文' >> 8 & 0xff, '文' & 0xff, 'a', 'b', 'c', '\0'}
|
一派护法 十九级 |
因此,在修改多字节字符串中的单个字符时,最好使用strcpy函数,不要直接修改数组元素。 例如要把字符数组char str[] = "中文abc";中的“文”字改成“国”字,应该写成: char str[] = "中文abc"; char *pointer = strstr(str, "文"); // 该指针指向“文”字的开头 memcpy(pointer, "国", sizeof("国") - 1); // 把“国”复制到“文”字那里覆盖掉,但不复制“国”字最后的\0标记 cout << str << endl; 输出:中国abc
如果直接修改数组元素,则是: char str[] = "中文abc"; str[2] = (char)('国' >> 8); str[3] = (char)('国' & 0xff); cout << str << endl; 不过,这样写会有兼容性问题。该段程序在Linux下用g++编译器编译后,运行会出错。
|
一派护法 十九级 |
在上述程序中用的是memcpy而不是strcpy。其实这两个函数差不多,只不过strcpy会把“国”字末尾的\0也复制过去覆盖掉,最终的字符串成了“中国”(即“中国\0bc\0”)而不是“中国abc”。 用memcpy的好处就是可以指定复制的长度。sizeof减1后就可以把那个\0减掉,不复制它。
|
一派护法 十九级 |
下面一段C++程序演示了如何手动将wchar_t字符串转换为UTF8格式存放到char数组中并写入txt文件: wchar_t wstr[] = L"这是一段中文字符串。"; wchar_t *wch; char ch[3]; FILE *fp; fopen_s(&fp, "file.txt", "wb"); for (wch = wstr; *wch != '\0'; wch++) { ch[0] = 0xe0 + (*wch >> 12); ch[1] = 0x80 + ((*wch >> 6) & 0x3f); ch[2] = 0x80 + (*wch & 0x3f); fwrite(ch, sizeof(ch), 1, fp); } fclose(fp); 上面这一段程序只能正确的转换U+0800~U+FFFF之间的字符。
|