C++源文件的编码问题

假设你准备用C/C++写一个"hello world",假设想把"hello world"替换成"世界,你好",或者你准备加一行中文注释。

你打算把C/C++的原文件以什么编码保存? utf-8? GBK/CP936/GB18030?

假设你打算以GBK方式保存,那么gcc编译的时候也是先转码成utf-8然后再编译,如果你编译的时候忘记加-finput-charset=gbk,那么一个潜在的BUG等着你。(见下面的例子)

假设你打算以UTF-8方式保存,VC怎么知道这个文件是UTF-8呢?它是看文件开头有没有BOM。UTF-16的BOM最大的功能是标志字节序,而UTF-8因为是字节序无关的,所以我从来不加BOM。如果你仅靠文件开头几个字符判断文件是什么编码,那么很遗憾,除非你只活在自己的世界里否则这是不可能的。文件编码类型多了去了,你见过GBK/ISO8859-1有BOM吗?那么给UTF-8加BOM意义何在呢?

假设你打算以UTF-8 with BOM的方式保存,那么低版本的gcc 是编译不了的。据我测试,gcc 4.4可以,gcc 4.1不行。

最后说VC的一个很常见但是很严重的警告"warning:C4819: 该文件包含不能在当前代码页(936)中表示的字符。请将该文件保存为 Unicode 格式以防止数据丢失"

这通常意味着编译器在以错误的编码打开源文件。大多数人都忽视了。可如果你看完这个测试例子也许会改变想法。

代码很短:

#include <iostream> 
enum AlienState{ 
        ALIVE, //活着 
        DIEING, //爆炸中 
        DEADED, 
} ;

int main(int argc,char* argv[]){ 
    std::cout<<DEADED<<std::endl; 
    return 0; 
}

看着似乎都对,但是这个代码在VC下就是编译不过去。

enumtest\main.cpp(8) : error C2065: "DEADED": 未声明的标识符

因为我保存的时候是utf-8 without BOM。如果你忽略了C4819警告,这意味着即便代码编译/链接完成了,但是它的某些语句可能因为编码错误而被编译器(当作注释)悄悄的丢弃了。这是一个非常巨大的风险。

微软对这个问题的反应非常冷淡,https://connect.microsoft.com/VisualStudio/feedback/details/341454/compile-error-with-source-file-containing-utf8-strings-in-cjk-system-locale,他认为这不是问题,所以标为"不会修复"。

"our suggestion for fixing this issue would be to use a BOM"

所以,最终,唯一正确的方法就是:把所有源文件以UTF-8 with BOM方式保存。但是我想大部分开源组织都不会接受这个建议。至少他们目前无法抛弃gcc 4.1。gcc 4.1依然是red hat 企业版默认的C/C++编译器。

此博客中的热门博文

在windows下使用llvm+clang

少写代码,多读别人写的代码

tensorflow distributed runtime初窥