UTF-8 and UTF-8 without BOM

来源:互联网 发布:那个答题软件好用 编辑:程序博客网 时间:2024/06/05 16:55

原文地址:http://zadecn.wordpress.com/2009/11/02/所见非所jjj得-c的代码编码encoding/

CPP的源代码是文本文件,所以可以保存为各种形式(format),例如ASCII编码,UTF-8,UTF-8 without BOM等.那么这些编码会带来一些问题吗?

因为习惯了在windows下编码,一致没有遇到类似的问题;最近在linux下使用g++,遇到这些问题,总结一下.

我们先看几个现象:

测试源代码非常的简单:

#include <iostream>
#include <string>

int main(){

    std::string text("123中国");   

    for(std::string::const_iterator itr = text.begin(); itr != text.end(); ++ itr){
        std::cout << (int)(*itr) << ‘,’;
    }
    std::cout << std::endl; 
    return 0;
}

对于上面的代码,我们保存为3种不同的编码格式:ASCII(main.cpp),UTF-8(main2.cpp),UTF-8 without BOM(main3.cpp),在windows xp + VC2008的测试环境下,结果如下:

ASCII输出: 49,50,51,-42,-48,-71,-6
UTF-8输出:49,50,51,-42,-48,-71,-6
UTF-8 without BOM输出:49,50,51,-28,-72,-83,-27,-101,-67

在ubuntu9.04+g++4.33的测试环境下,结果如下:

ASCII输出: 49,50,51,-42,-48,-71,-6
UTF-8输出:编译错误(消息:main2.cpp:1: error: stray ‘357’ in program…)
UTF-8 without BOM输出:49,50,51,-28,-72,-83,-27,-101,-67

我们先来解释这个编译错误的问题.

g++不支持UTF-8格式,这点倒是在Google上轻而易举的找到.但是比较诡异的是,如果你使用文本编辑器打开main2.cpp,你是看不到’357′的,原因是UTF-8格式是在文件的最开头添加了三个特殊的字符:EF,BB,BF,这是微软的一种特殊的做法,而这种做法并没有被g++认可,但是却被linux下的一般的文本编辑器所认可.所以导致编译错误.

当我们使用IDE的时候,比如Eclipse+CDT,可以设置workspace的编码方式,比如设置为GBK,这样你可以在IDE环境下看到和上面一样的源代码内容(main2.cpp),但是就是无法正确编译,也就是说,你的所见(在文本编辑器中见到的结果)并非你的所得(编译后的可执行文件的结果),这虽然违反直觉,但并不违反逻辑.

我们再看看为什么相同的源代码内容,不同的编码方式,却产生不同的结果输出.

在ASCII的编码方式,一个汉字被编译为两个char,放在特定的文字常量区;在UTF-8 without BOM的方式下,一个汉字被编译为3个char,放在特定的文字常量区.在这点上,VC和g++倒是完全的一致.

我们看看标准是如何表达这个问题的(C++2003的标准,ISO/IEC 14882,46页,2.13.2的第5部分):

A universal-character-name is translated to the encoding, in the execution character set, of the character named. If there is no such encoding, the universal-character-name is translated to an implementation defined encoding.

(2.2)

The values of the members of the execution character sets are implementation-defined, and any additional members are locale-specific

对于除了基本字符(ASCII)以外的字符,标准的定义是"implementation-defined",也就是说,由实现者(VC,g++等)定义. 从我们的测试结果来看,实现者(VC,g++)都是采用和源代码一样的编码方式,把文字常量编译到特定的可执行文件的文字常量区.

简单的说,不同的输出结果是因为编译器根据不同编码方式把相同的文字常量编译为不同的文字常量值存放到可执行文件中所导致的.

我们得到的结论是:

为了跨平台编译,我们使用同一的UTF-8 without BOM格式;或者至少对于含有汉字(universal-character-name)常量的源文件,要存储为UTF-8 without BOM的格式.这样才产生期望的结果输出.

-----------------------------------------------

使用VIM来解决问题.

:set nobomb

:w

编码的问题,真让人头疼.