拨开字符编码的迷雾--编译器如何处理文件编码

来源:互联网 发布:淘宝无节操买家秀图片 编辑:程序博客网 时间:2024/04/30 02:15

1. Visual Studio字符集

使用Visual Studio创建的C++工程可以在工程属性配置属性-->常规中配置字符集:使用Unicode字符集(默认)、使用多字节字符集这个设置项不对字符编码产生直接的影响(注意这里的“直接”二字,第3节会说到),只会在工程属性配置属性-->C/C++-->预处理器加入相应的宏:

使用Unicode字符集 --> _UNICODE和UNICODE宏使用多字节字符集   --> _MBCS宏

这几个宏一般用来判断是使用char还是wchar_t,在系统API中使用比较多,如MessegeBox通过是否定义了UNICODE宏来决定是使用LPCSTR还是LPCWSTR(LPCSTR即const char, LPCWSTR即const wchar_t):

#ifdef UNICODE#define MessageBox  MessageBoxW#else#define MessageBox  MessageBoxA#endif // !UNICODE

2. char和wchar_t

上面提到了,定义API时通过判断UNICODE宏是否定义来决定是使用char还是wchar_t,那么char和wchar_t有什么不同了?

char和wchar_t是标准C/C++字符类型,并不是windows特有的。 char固定占1个字节,wchar_t固定占2个字节,从内存的角度来看,char、wchar_t和其他数据类型一样,只是代表一段内存块,用来存储固定长度的二进制0或1。 在编程时,我们一般习惯于将字符串储到char或wchar_t定义的内存空间中,将整形存储在int定义的内存空间中。

所以,用char还是wchar_t来存储字符,只是内存分配和数据存储上面的事情,它们本身也是与字符编码无直接关系的( 同样注意这里的“直接”二字,第3节会说到)。

3. 编译器如何处理硬编码字符

VC++编译器编译源代码的步骤中,涉及编码处理的步骤主要有2个:
第1步:预处理
1.1) 读取源文件,判断源文件采用的字符编码类型。

编译器判断源文件编码类型的步骤为:1. 若文件开始处有BOM(EF BB BF),则判定为UTF-8编码;2. 若没有BOM,则试图从文件的前8个字节来判断文件是否像UTF-16编码,如果像,则就判断为UTF-16编码。3. 如果既没BOM,也不是UTF-16编码,则使用系统当前的代码页。

1.2) 将源文件内容转成源字符集(Source Character Set),默认为UTF-8编码。

第2步:链接
2.1) 将1.2中得到的UTF-8转为执行字符集(Execution Character Set):

  • 对于宽字符串(即C/C++中以L标记的串,如L"abc", L'中'),执行字符集为UTF-16编码。
  • 对于窄字符串(和宽字符串对应,即不以L标记的串),执行字符集为系统当前的代码页。

编译器处理字符编码过程

现在我们就可以说清楚Visual Studio字符集设置、char、wchar_t是如何间接影响到字符编码的了:

Visual Studio字符集设置      |决定声明哪一个宏(UNICODE还是_MBCS宏)      |宏又决定了API参数使用char还是wchar_t      |编译器在进行【执行字符集】编码时对char和wchar_采用不同的处理方式,从而对字符编码产生了影响。

4. 彻底避免硬编码字符乱码

通过第3节的说明,很容易知道,要开发支持多语言,在任意语言(系统代码页)的windows环境下都正常编译,且运行起来没有乱码的程序,需要遵循如下原则:

  1. 代码文件采用UTF-8 with BOM编码。
  2. Visual Studio字符集设置为Unicode字符集。
  3. 使用wchar_t。

做到上面3步,你的代码被别人从github上clone下来编译,不会因为你代码中含有中文等字符,产生类似error C2015这样的编译错误,更不会产生乱码。

本文介绍的方法只用来解决硬编码字符乱码的问题,至于数据传输中的乱码,需要统一字符编码来解决。

参考: https://blogs.msdn.microsoft.com/vcblog/2016/02/22/new-options-for-managing-character-sets-in-the-microsoft-cc-compiler

原创粉丝点击