编程中经常会遇到这三种字符编码形式的相互转换问题,以至于许多第三方的库不明原因的调用失败,其实很多都是由于第三方库支持的是utf-8而不是windows默认支持的utf-16导致的。
下面介绍一下windows系统下常见的这三种字符编码方式。
GB2312
是我们国家自己国标的汉字编码字符集,该字符集以一个16位的2进制数据单元表示一个汉字,所以能够将两个char型数据单元保存一个汉字。
微软的Windows操作系统汉字的编码字符集支持GB2312。这就是为什么我们用:
const char* pChar = “中文”;
printf(pChar);
能够正确显示中文的原因。
但是,假如我们现在要将程序转换为一种除了中文和标准ASCII字符之外的文字时(比如说韩文),由于韩文不能被GB2312解析,所以就会产生乱码。
这就是为什么微软推荐采用Unicode的原因。因为Unicode包含了所有人类已知的文字字符集,理论上可以解析所有文字。
Unicode
Unicode字符集实际上是国际标准 ISO 10646 的一个子集。Unicode字符集是由Unicode协会公布的
ISO 10646定义了 通用字符集 (Universal Character Set, UCS). UCS 是所有其他字符集标准的一个超集。ISO 10646 定义了一个 31 位的字符集. 然而, 在这巨大的编码空间中, 迄今为止只分配了前 65534 个码位 (0x0000 到 0xFFFD). 这个 UCS 的 16位子集称为基本多语言面 (Basic Multilingual Plane, BMP). 将被编码在 16 位 BMP 以外的字符都属于非常特殊的字符(比如象形文字), 且只有专家在历史和科学领域里才会用到它们.
UTF-16
UCS 和 Unicode 只是分配整数给字符的编码表. 现在存在好几种将一串字符表示为一串字节的方法. 最显而易见的两种方法是将 Unicode 文本存储为 2 个 或4 个字节序列的串. 这两种方法的正式名称分别为 UCS-2 和 UCS-4. Windows的Unicode表示方式极为UCS-2,即用两个字节表示一个Unicode字符。
除非另外指定, 否则大多数的字节都是Bigendian convention. 将一个 ASCII 或 Latin-1 的文件转换成 UCS-2 只需简单地在每个 ASCII 字节前插入 0x00. 如果要转换成 UCS-4, 则必须在每个 ASCII 字节前插入三个 0x00.
Windows内部使用UCS-2标准,并用UTF-16实现。在基本多语言平面内定义的符号((Basic MultilingualPlane, BMP),或称第零平面(Plane 0)),使用2个字节表示。
所以,Windows中使用的wchar_t的单位为2个字节,一个ASCII字符也要用两个字节表示。
Java采用的也是UTF-16。
UTF-8
在 Unix 下使用 UCS-2 (或 UCS-4) 会导致非常严重的问题. 用这些编码的字符串会包含一些特殊的字符, 比如 '\0' 或 '/', 它们在 文件名和其他 C 库函数参数里都有特别的含义. 另外,大多数使用 ASCII 文件的 UNIX 下的工具, 如果不进行重大修改是无法读取 16 位的字符的. 基于这些原因, 在文件名, 文本文件, 环境变量等地方,UCS-2 不适合作为Unicode 的外部编码.
在 ISO 10646-1 Annex R 和RFC 2279 里定义的UTF-8 编码没有这些问题. 它是在 Unix 风格的操作系统下使用 Unicode 的明显的方法.
UTF-8 有一下特性:
- UCS 字符 U+0000 到 U+007F (ASCII) 被编码为字节 0x00 到 0x7F (ASCII 兼容). 这意味着只包含 7 位 ASCII 字符的文件在 ASCII 和 UTF-8 两种编码方式下是一样的.
- 所有 >U+007F 的 UCS 字符被编码为一个多个字节的串, 每个字节都有标记位集. 因此, ASCII 字节 (0x00-0x7F) 不可能作为任何其他字符的一部分.
- 表示非 ASCII 字符的多字节串的第一个字节总是在 0xC0 到 0xFD 的范围里, 并指出这个字符包含多少个字节. 多字节串的其余字节都在 0x80 到 0xBF 范围里. 这使得重新同步非常容易, 并使编码无国界, 且很少受丢失字节的影响.
- 可以编入所有可能的 231个 UCS 代码
- UTF-8 编码字符理论上可以最多到 6 个字节长, 然而 16 位 BMP 字符最多只用到 3 字节长.
- Bigendian UCS-4 字节串的排列顺序是预定的.
- 字节 0xFE 和 0xFF 在 UTF-8 编码中从未用到.
注意在多字节串中, 第一个字节的开头"1"的数目就是整个串中字节的数目.
UTF-8在web协议和Unix族的操作系统中广泛使用。ASCII不作变换, 其他字符做变长编码, 每个字符1-3 byte.
搞清了这三种编码方式,下面再谈一下如何相互转化的问题。
//wchar_t转成UTF-8
inline string ConvertWChar2UTF8( const wchar_t* a_szSrc )
{
const int nszBuffer = WideCharToMultiByte( CP_UTF8, 0, a_szSrc, -1, NULL, 0, NULL, NULL );
char* Buffer = new char[nszBuffer];
WideCharToMultiByte( CP_UTF8, 0, a_szSrc, -1, Buffer, nszBuffer, NULL, NULL );
string strReturn = Buffer;
delete[] Buffer;
return strReturn;
} ;
参考文献:
http://unicode.org/faq/utf_bom.html
http://zh.wikipedia.org/wiki/UTF-16
http://zh.wikipedia.org/wiki/UTF-8
http://www.linuxforum.net/books/UTF-8-Unicode.html
http://zh.wikipedia.org/wiki/GB_2312
http://zhidao.baidu.com/question/27910414.html