在C/C++程序(VS2010)里,UNICODE字符串对应wchar_t,ANSI字符串对应char。比如字符串 L"汉" 的二进制值是0x6c49,与UNICODE编码真值一致。字符串 "汉" 的二进制值是0xba,0xba,与GBK编码真值一致。
在字符串操作中,有时需要在UNICODE字符串和ANSI字符串之间进行转换。下面以输出一个字符串到文本文件为例,讲讲这个转换过程(输出到console稍微有点特殊,后面讨论)。根据输出操作中源和目标的字符串模式,可以分成以下4种情况:
情况1,源为ANSI模式,目标为ANSI模式。
这是我们最常用的情况,源和目标模式一致,无需转换。
...
fputs("abc汉字", fp);
...
在上面的代码里,源是ANSI字符串"abc汉字",以默认方式打开的目标fp也是ANSI模式。
情况2,源为ANSI模式,目标为UNICODE模式。
这种情况是不允许的,从微软代码的注释来看,是为了防止ANSI系列函数操作UNICODE文件。
情况3,源为UNICODE模式,目标为ANSI模式。
为了使源为UNICODE模式,我们只能使用wchar_t字符串,比如下面的代码:
...
fputws(L"abc汉字", fp);
...
源是UNICODE字符串(UTF-16LE编码方式),以默认方式打开的目标fp是ANSI模式。
通过调用wctomb函数把UNICODE字符串转成基于code page的ANSI字符串,这也是目标fp可以接受的字符串。
wctomb函数的行为是跟locale相关的,如果是c locale,它会逐个判断wchar_t,如果值大于255,则是出错,否则强制转换成char;如果不是c locale,比如chinese locale,它会以该locale对应的code page(比如936)为参数调用WideCharToMultiByte进行转换。
情况4,源为UNICODE模式,目标为UNICODE模式。
做为源使用的wchar_t字符串,其编码模式是UTF-16LE。做为目标,则有两种选择,UTF-16LE和UTF-8。
...
fp = fopen("a.txt", "w,ccs=UTF-16LE");
fputs(L"abc汉字", fp);
...
fp = fopen("a.txt", "w,ccs=UTF-8");
fputs(L"abc汉字", fp);
...
如果目标是UTF-16LE编码模式,则与源是一致的,无需转换。如果目标是UTF-8编码模式,则需要以UTF-8 code page为参数调用WideCharToMultiByte进行转换。
上面是操作普通文本文件,接下来是操作console,同样可以分成4种情况:
情况1,源为ANSI模式,目标为ANSI模式。
虽然源和目标模式一致,但还需要根据locale进行不同处理。如果是c locale,则无需转换,直接写入(用WriteFile写到console对应的句柄)。否则,需要分两步处理。第一步,调用mbtowc将源字符串(ANSI模式)转成临时的UNICODE字符串(UTF-16LE)。第二步,以console code page为参数调用WideCharToMultiByte将临时的UNICODE字符串转成目标字符串(ANSI模式),再写入(WriteFile)。
这里需要注意,虽然都是ANSI模式,但源字符串是基于相应locale的code page,目标字符串是基于console的code page(这两个code page往往是一样的)。
mbtowc的行为是locale相关的,如果是c locale,则将char强制转成wchar_t(在上面的第一步中,显然不会遇到这种情况)。否则,以相应locale的code page为参数调用MultiByteToWideChar进行转换,将ANSI字符串转成UNICODE字符串(UTF-16LE)。
情况2,源为ANSI模式,目标为UNICODE模式。
这种情况也是不允许的。
情况3,源为UNICDOE模式,目标为ANSI模式。
这里的源字符串也只能是UTF-16LE编码方式。如果是c locale,则逐个判断wchar_t,值大于255是出错,否则强制转成char写入(WriteFile)。如果不是c locale,这是最复杂的一种情况,需要经过三次转换:
首先,以相应locale的code page为参数调用WideCharToMultiByte将源字符(UNICODE模式)串转成临时的ANSI字符串。
接着,调用mbtowc将临时的ANSI字符串转成临时的UNICODE字符串(这个字符串其实跟源字符串是一致的,但代码的流程就是这样走的)。
最后,以console code page为参数调用WideCharToMultiByte将临时的UNICODE字符串转成目标字符串(ANSI模式),再写入(WriteFile)。最后这两步与情况1的两步是一样的。
情况4,源为UNICODE模式,目标为UNICODE模式。
做为源使用的wchar_t字符串,其编码模式是UTF-16LE。做为目标,则有两种编码模式,UTF-16LE和UTF-8。无需区分这两种情况,windows已经帮我们处理好了,直接调用WriteConsoleW写入。
...
_setmode(_fileno(stdout), _O_U16TEXT);
fputws(L"1汉1", stdout);
...
_setmode(_fileno(stdout), _O_U8TEXT);
fputws(L"1汉1", stdout);
...