2011年9月

(转)C/C++源文件编码方式

好文一篇,转来共赏^_^

"汉字"
GBK编码:BA BA , D7 D6
UTF-8编码:E6 B1 89, E5 AD 97
UTF-16BE编码:6C 49, 5B 57

2356注:文中的GCC指GCC中的C/C++编译器,CL指Visual C++编译器

两种常用编译器gcc,cl中对Unicode字面值的实现:

GCC
gcc中跟编码方式转换有关的三个编译选项:

-finput-charset=charset,此选项指定源文件本身的编码方式,默认为UTF-8(有无BOM均可)。例如当我们的源代码文件保存为GBK时,则也应当将此选项的值指定为GBK。
-fwide-exec-charset=charset,此选项指定宽字符或宽字符串的字面值常量的内部编码方式,默认为UTF-32或UTF-16,对应wchar_t的宽度。wchar_t的宽度依赖平台实现,windows 实现为2字节宽,linux 实现为4字节宽。例如指定此选项为GBK,则宽字符或宽字符串常量将会以GBK编码方式存储而不是默认的UTF-32或UTF-16编码方式。
-fexec-charset=charset,此选项指定窄字符或窄字符串的字面值常量的内部编码方式,默认为UTF-8。例如指定此选项为GBK,则窄字符或窄字符串常量将会以GBK编码方式存储而不是默认的UTF-8编码方式。

有了以上铺垫,下面两条语句的意义就很清楚了:
char cstr[] = "汉字"; //将"汉字"由-finput-charset指定的编码方式转换成由-fexec-charset指定的编码方式。
wchar_t wstr[] = L"汉字"; //将"汉字"由-finput-charset指定的编码方式转换成由-fwide-exec-charset指定的编码方式。

注1:gcc在4.4.0版本以前存在bug,不能编译带BOM的UTF-8源文件。参见http://gcc.gnu.org/bugzilla/show_bug.cgi?id=33415

注2:Qt自带的mingw gcc 4.4.0貌似有bug,不能支持-finput-charset选项;TDM gcc 4.5.1则没有这个问题。
注3:即便TDM gcc 也不能支持UTF-16,无论BE或者LE。症状是好像只要包含了头文件--无论是标准库的头文件还是Qt的头文件,就无法编译通过,不包含头文件则是可以编译通过的。

CL
cl 没有提供类似gcc指定编码方式的编译选项,只能使用默认值或靠自动识别:

对应gcc的-finput-charset,cl 能自动识别源文件的编码方式,cl 支持的源文件编码方式有:UTF-16(BE,LE均可,有无BOM均可),UTF-8(带BOM),除此之外的源文件均认为是ANSI编码方式(包括不带BOM的UTF-8)。所以使用VC编译不带BOM的UTF-8文件时要特别注意,vc会将此文件当做ANSI也就是GBK编码格式。否则经常会出这样的警告:该文件包含不能在当前代码页(936)中表示的字符。请将该文件保存为 Unicode 格式以防止数据丢失。

对应gcc的-fwide-exec-charset, cl 固定使用UTF-16编码方式。
对应gcc的-fexec-charset, cl 固定使用ANSI编码方式。

再看同样的语句在 cl 下的行为:

对于源文件的编码方式为UTF-16BE,UTF-16LE, UTF-8(带BOM):
char cstr[] = "汉字"; //将源文件中的"汉字"从当前编码方式转换成ANSI编码方式,即GBK。
wchar_t wstr[] = L"汉字"; //将源文件中的"汉字"从当前编码方式转换成UTF-16编码方式。

对于其他编码方式的源文件(包括无BOM的UTF-8),实际上会被当做ANSI编码,即GBK:
char cstr[] = "汉字"; //认为已经是ANSI编码了,故没有转换发生,cstr中依旧是"汉字"的原始编码。例如,如果源文件的编码方式为无BOM的UTF-8,则cstr中依旧是"汉字"的UTF-8编码。
wchar_t wstr[] = L"汉字"; //无论源文件采用何种编码方式,都将"汉字"当做ANSI编码,转换成UTF-16编码。这样硬转的结果当然是除了当前是GBK编码时能正常转换,其余各种编码方式得到的只能是乱码一堆。

有关cl对unicode的支持请参考:http://msdn.microsoft.com/en-us/library/xwy0e8f2(v=VS.90).aspx

C++1x

面对如此混乱的局面,c++1x提供了更多字符串字面值表示法:
"string of char characters in some implementation defined encoding" - char
u8"string of utf8 chars" - char
u"string of utf16 chars" - char16_t
U"string of utf32 chars" - char32_t
L"string of wchar_t in some implementation defined encoding" - wchar_t