Unicode快速指引

Posted on 2010-03-13 23:45  红袍艾德温  阅读(261)  评论(0)    收藏  举报

最近在弄的一个东西,由于一开始就没有规范好字符编码,结果遇到不少的困扰。《Windows核心编程》第一章就是讲字符编码,可见这东西的重要。于是痛定思痛,决定用UTF-8统一所有内部编码。重温了一些知识,整理出来备忘。

1,简介

Unicode学名"Universal Multiple-Octet Coded Character Set",简称UCS,是一套由国际组织维护的字符编码。UCS为现存的每一个字符都赋予一个唯一码值(code point),通常表示为U+xxxx,其中xxxx为对应的16进制码值

UCS有两种格式:UCS-2 和 UCS-4。UCS-2为2字节编码,范围从U+0000~U+FFFF;UCS-4为4字节编码,范围从U+00000000~U+7FFFFFFFF。目前Unicode4.0标准中,U+0000~U+FFFF区间已经包含了世界上所有语言的常用文字,简称BMP(Basic Multilingual Plane),加上其它的特殊符号扩展,最高也只用到了U+0010FFFF

2,UTF编码

UCS字符在计算机中的表示方式常用的有UTF-8,UTF-16,UTF-32。UTF-32用32位来表示单个UCS字符,跟UCS-4的码值一一对应。UTF-16以16位为单元来编码UCS字符,U+0000~U+FFFF范围内用单个16位值来表示,跟UCS-2一一对应,而U+10000~U+10FFFF范围内的字符则需要用2个连续的16位值来表示;UTF-8以8位为单元进行编码,跟UTF-16类似,需要用到1~6个连续的8位值来表示单个UCS字符(实际上由于目前UCS最高只用到了U+10FFFF,所以UTF-8编码的单个字符的长度不会超过4Byte)。具体算法如下表所示

UCS-4到UTF-16的转换表:

U+00000000~U+0000FFFF
xxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxx
U+00010000~U+0010FFFF
000uuuuuxxxxxxxxxxxxxxxx
110110wwwwxxxxxx 110111xxxxxxxxxx

注1:wwww=uuuuu-1

注2:U+DB00(1101100000000000)~U+DBFF(110110FFFFFFFFFF)称为High Surrogate,U+DC00(1101110000000000)~U+DFFF(110111FFFFFFFFFF)称为Low Surrogate。Surrogate区间只用于UTF-16中表示BMP之外的字符。BMP中除Surrogate之外的也被称为character's scalar value

UCS-4到UTF-8的转换表:

U+00000000~U+0000007F
0xxxxxxx
U+00000080~U+000007FF
110xxxxx 10xxxxxx
U+00000800~U+0000FFFF
1110xxxx 10xxxxxx 10xxxxxx
U+00010000~U+001FFFFF
11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
U+00200000~U+03FFFFFF
111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
U+04000000~U+7FFFFFFF
1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx

3,BOM

UTF-16和UTF-32都有字节序的问题,需要区分big endian和little endian。通常这两种编码的文件都会在开头额外添加一个U+FEFF字符。因为FFFE是UCS中不存在的字符,在实际中不应该出现,这样在读取文件的时候首先检测一下,如果是FEFF,就表明是Big-Endian的;如果是FFFE,就表明是Little-Endian的。这个就称为BOM(Byte Order Mark)。

UTF-8无字节序的问题,但也可以加上BOM来表明编码方式。U+FEFF的UTF-8编码是EF BB BF,如果在文件头读到这个,就表示该文件是UTF-8编码的

4,跨平台

Windows内部使用UTF-16编码,VC将单字节字符串常量编译成locale指定的本地编码(中文系统默认为GBK,可以通过编译器指令#pragma setlocale(LOC)来指定本地编码,但不支持UTF-8),wchar_t是16位,宽字节字符串常量用UTF-16进行编码。
Linux内部用UTF-8编码,GCC将单字节字符串编译为UTF-8,wchar_t为32位,宽字节字符串常量用UCS-4编码

MinGW下,单字节字符是utf-8编码的,但wchar_t跟VC一样是16位,宽字节字符串也是用UTF-16编码

在我看来,考虑到跨平台的话,程序内部使用UTF-8编码是不错的选择

另外,对于C源文件的编码方式,一般来说保存为UTF-8会更好,这样即使里面包含中文字符,其它国家或地区的程序员打开你的文件也不会看到乱码。但遗憾的是,虽然VC和GCC都支持UTF-8,但前者只能识别带BOM的,后者不能有BOM,残念...

5,相关资料

基础知识:《谈谈Unicode编码,简要解释UCS、UTF、BMP、BOM等》(请google)
Wiki百科条目: http://en.wikipedia.org/wiki/Unicode
Unicode 4.0 标准:http://www.unicode.org/versions/Unicode4.0.0/UnicodeBookTOC.pdf#G1257283
其中第三章详细介绍了UTF编码:http://www.unicode.org/versions/Unicode4.0.0/ch03.pdf
UCS-4/UTF-16/UTF-8编码转换的C代码:http://ftp.kaist.ac.kr/hangul/mirror/ftp.unicode.org/PROGRAMS/CVTUTF/