使用VC编程时,常常会碰到显示汉字乱码的问题。网上可以搜到的问题解答,是调用setlocale。然后就正常了,但是一直不理解其原理.......
最近看到一篇好文章《刨根究底字符编码》,有点醍醐灌顶的感觉。结合实际应用,谈谈自己的理解,不知道对不对。
首先,影响因素有两点,一是VC项目属性的UNICODE宏预定义(或"使用多字节字符集",以下简称ANSI),二是setlocale函数的设置。前者决定了一个汉字存储时的字符编码方案,后者决定了_tprintf等显示汉字时的解码方案,只有存储编码方案与显示解码方案一致,才不会显示乱码。以“万”字为例,下面是对应关系
setlocale参数2 | VC预定义 ANSI | _tprintf显示结果 | VC预定义UNICODE | _tprintf显示结果 |
"C" | cd f2 | 万 | 07 4e | ? |
"" | cd f2 | 万 | 07 4e | 万 |
"chs" | cd f2 | 万 | 07 4e | 万 |
从表中可以看到,“万”字在VC预定义了ANSI时,其存储编码都是 cd f2,在预定义 UNICODE时,编码都是07 4e。为何setlocale( , "C") +预定义UNICODE就显示乱码,但setlocale( , "")或 setlocale( , "chs")就显示都正常呢?
乱码原因是,setlocale(, "C")决定了_tprintf的解码方案是ANSI,但是UNICODE宏又决定了存储的编码方案,即本来_tprintf想打印"万",需要的编码是cd f2,但是由于定义了UNIOCDE,只能给它07 4e,所以就乱了。
那存储编码和显示解码是怎么工作的呢?
这涉及到三套编码,一是VC中用中文输入法输入“万”时,输入法自带的"万"的编码,叫做“输入码”,我们敲击键盘(以全拼为例),输入"w","a","n",输入法会根据输入的组合找到自带的输入码表中“万”的输入码,然后映射到“万”的存储字符编码,这就是第二套编码,把"万"字的字符编码存储在内存和磁盘源文件中,第二套编码是安装系统时,就有了,属于系统(比如Windows自带的ANSI编码或utf16编码),最后需要在显示器上显示出"万",就是_tprintf,它根据setlocale设置的解码方案,如果解码方案与存储编码方案一致,则能识别出是“万”字,然后就需要将"万”映射为第三套编码,就是字体编码(字体库或点阵图),按该编码输出到显示器上。