

本文写给程序员的趣味读物。所谓趣味是指可以比较轻松地了解一些原来不清楚的概念,增进知识,整理这篇文章的动机是两个问题:
问题一:
使用Windows记事本的“另存为”,可以在GBK、Unicode、Unicode big endian和UTF-8这几种编码方式间相互转换。同样是txt文件,Windows是怎样识别编码方式的呢?
很早前就发现Unicode、Unicode big endian和UTF-8编码的txt文件的开头会多出几个字节,分别是FF、FE(Unicode),FE、FF(Unicode big endian),EF、BB、BF(UTF-8)。但这些标记是基于什么标准呢?
问题二:
最近在网上看到一个 ConvertUTF.c,实现了UTF-32、UTF-16和UTF-8这三种编码方式的相互转换。对于Unicode(UCS2)、GBK、UTF-8这些编码方式,我原来就了解。但这个程序让我有些糊涂,想不起来UTF-16和UCS2有什么关系。
查了查相关资料,总算将这些问题弄清楚了,顺带也了解了一些Unicode的细节。写成一篇文章,送给有过类似疑问的朋友。本文在写作时尽量做到通俗易懂,但要求读者知道什么是字节,什么是十六进制。
0、big endian和little endian
big endian和little endian是CPU处理多字节数的不同方式。例如“汉”字的Unicode编码是6C49。那么写到文件里时,究竟是将6C写在前面,还是将49写在前面?如果将6C写在前面,就是big endian。还是将49写在前面,就是little endian。
“endian”这个词出自《格列佛游记》。小人国的内战就源于吃鸡蛋时是究竟从大头(Big-Endian)敲开还是从小头(Little-Endian)敲开,由此曾发生过六次叛乱,其中一个皇帝送了命,另一个丢了王位。
我们一般将endian翻译成“字节序”或"端序",将big endian和little endian称作“大尾”和“小尾”。
1、字符编码、内码,顺带介绍汉字编码
字符必须编码后才能被计算机处理。计算机使用的缺省编码方式就是计算机的内码。早期的计算机使用7位的ASCII编码,为了处理汉字,程序员设计了用于简体中文的GB2312和用于繁体中文的big5。
GB2312(1980年)一共收录了7445个字符,包括6763个汉字和682个其它符号。汉字区的内码范围高字节从B0-F7,低字节从A1-FE,占用的码位是72*94=6768。其中有5个空位是D7FA-D7FE。
GB2312 支持的汉字太少。1995年的汉字扩展规范GBK1.0收录了21886个符号,它分为汉字区和图形符号区。汉字区包括21003个字符。2000年的 GB18030是取代GBK1.0的正式国家标准。该标准收录了27484个汉字,同时还收录了藏文、蒙文、维吾尔文等主要的少数民族文字。现在的PC平台必须支持GB18030,对嵌入式产品暂不作要求。所以手机、MP3一般只支持GB2312。
从ASCII、GB2312、GBK到 GB18030,这些编码方法是向下兼容的,即同一个字符在这些方案中总是有相同的编码,后面的标准支持更多的字符。在这些编码中,英文和中文可以统一地处理。区分中文编码的方法是高字节的最高位不为0。按照程序员的称呼,GB2312、GBK到GB18030都属于双字节字符集 (DBCS)。
有的中文Windows的缺省内码还是GBK,可以通过GB18030升级包升级到GB18030。不过GB18030相对GBK增加的字符,普通人是很难用到的,通常我们还是用GBK指代中文Windows内码。
这里还有一些细节:
GB2312的原文还是区位码,从区位码到内码,需要在高字节和低字节上分别加上A0。
在DBCS中,GB内码的存储格式始终是big endian,即高位在前。
GB2312 的两个字节的最高位都是1。但符合这个条件的码位只有128*128=16384个。所以GBK和GB18030的低字节最高位都可能不是1。不过这不影响DBCS字符流的解析:在读取DBCS字符流时,只要遇到高位为1的字节,就可以将下两个字节作为一个双字节编码,而不用管低字节的高位是什么。
2、Unicode、UCS和UTF
前面提到从ASCII、GB2312、GBK到GB18030的编码方法是向下兼容的。而Unicode只与ASCII兼容(更准确地说,是与ISO-8859-1兼容),与GB码不兼容。例如“汉”字的Unicode编码是6C49,而GB码是BABA。
Unicode 也是一种字符编码方法,不过它是由国际组织设计,可以容纳全世界所有语言文字的编码方案。Unicode的学名是"Universal Multiple-Octet Coded Character Set",简称为UCS。Unicode字符集(Unicode Character Set)是字符(character)到代(编)码点(code point)的抽象关系映射。它和字符在内存中的特定表示方法没有关系,也就是说在谈论字符时可以不用考虑操作系统的区别,其在任何一个平台上都总是同一个实体。而平时讲的编码(encode)是指将字符的代码点按照特定形式存储到内存中的方式,由它建立起抽象字符映射和计算机物理实现之间的桥梁。可能认为谈到存储时应该使用“字节(byte)”这个术语,不过在我们谈论Unicode时应该用术语位组(octet)。不同的编码方式存储位组的方式不同,从另一个意义上来说,反过来把特定位组按照既定的编码方式转换成字符的过程就称为解码(decode)。
Unicode由一个或多个八位组组成,当谈到代码点的时候会用十六进制数字表示,如:字母A是U+0041,字母a是U+0061,而'国'是U+56FD,代码点还能判断知晓有关它本身的一些信息。它知道自己是否为大写字符还是小写字符,是字母还是数字,或者属于空格一类的空白符等等。一个编码点可以描述一个字符,但是它不是描述这个字符的编码。与字符有联系的编码点和该字符的实际编码结果无关。
UTF-8是“UCS Transformation Format 8-bit”的简写,是Unicode编码和一种实现方式。
更多码点的查询可参考:Character Code Charts
根据维基百科的记载:历史上存在两个试图独立设计Unicode的组织,即国际标准化组织(ISO)和一个软件制造商的协会(unicode.org)。ISO开发了ISO 10646项目,Unicode协会开发了Unicode项目。
在1991年前后,双方都认识到世界不需要两个不兼容的字符集。于是它们开始合并双方的工作成果,并为创立一个单一编码表而协同工作。从Unicode2.0开始,Unicode项目采用了与ISO 10646-1相同的字库和字码。
目前两个项目仍都存在,并独立地公布各自的标准。Unicode协会现在的最新版本是2005年的Unicode 4.1.0。ISO的最新标准是10646-3:2003。
UCS规定了怎么用多个字节表示各种文字。怎样传输这些编码,是由UTF(UCS Transformation Format)规范规定的,常见的UTF规范包括UTF-8、UTF-7、UTF-16。
IETF 的RFC2781和RFC3629以RFC的一贯风格,清晰、明快又不失严谨地描述了UTF-16和UTF-8的编码方法。我总是记不得IETF是 Internet Engineering Task Force的缩写。但IETF负责维护的RFC是Internet上一切规范的基础。
2.1、内码和code page
目前Windows的内核已经采用Unicode编码,这样在内核上可以支持全世界所有的语言文字。但是由于现有的大量程序和文档都采用了某种特定语言的编码,例如GBK,Windows不可能不支持现有的编码,而全部改用Unicode。
Windows使用代码页(code page)来适应各个国家和地区。code page可以被理解为前面提到的内码。GBK对应的code page是CP936。微软也为GB18030定义了code page:CP54936。但是由于GB18030有一部分4字节编码,而Windows的代码页只支持单字节和双字节编码,所以这个code page在Windows下是无法真正使用的。
3、UCS-2、UCS-4、BMP
UCS有两种格式:UCS-2和UCS-4。顾名思义,UCS-2就是用两个字节编码,UCS-4就是用4个字节(实际上只用了31位,最高位必须为0)编码。下面让我们做一些简单的数学游戏:
UCS-2有2^16=65536个码位,UCS-4有2^31=2147483648个码位。
UCS-4根据最高位为0的最高字节分成2^7=128个group。每个group再根据次高字节分为256个plane。每个plane根据第3个字节分为 256行 (rows),每行包含256个cells。当然同一行的cells只是最后一个字节不同,其余都相同。
group 0的plane 0被称作Basic Multilingual Plane, 即BMP。或者说UCS-4中,高两个字节为0的码位被称作BMP。
将UCS-4的BMP去掉前面的两个零字节就得到了UCS-2。在UCS-2的两个字节前加上两个零字节,就得到了UCS-4的BMP。而目前的UCS-4规范中还没有任何字符被分配在BMP之外。
4、UTF编码
UTF-8就是以8位为单元对UCS进行编码。从UCS-2到UTF-8的编码方式如下:
UCS-2编码(16进制) UTF-8 字节流(二进制)
0000 -007F 0xxxxxxx
0080 -07FF 110xxxxx 10xxxxxx
0800 -FFFF 1110xxxx 10xxxxxx 10xxxxxx
例如“汉”字的Unicode编码是6C49。6C49在0800-FFFF之间,所以肯定要用3字节模板了:1110xxxx 10xxxxxx 10xxxxxx。将6C49写成二进制是:0110 110001 001001, 用这个比特流依次代替模板中的x,得到:11100110 10110001 10001001,即E6 B1 89。
读者可以用记事本测试一下我们的编码是否正确。
UTF-16以16位为单元对UCS进行编码。对于小于0x10000的UCS码,UTF-16编码就等于UCS码对应的16位无符号整数。对于不小于 0x10000的UCS码,定义了一个算法。不过由于实际使用的UCS2,或者UCS4的BMP必然小于0x10000,所以就目前而言,可以认为 UTF-16和UCS-2基本相同。但UCS-2只是一个编码方案,UTF-16却要用于实际的传输,所以就不得不考虑字节序的问题。
5、UTF的字节序和BOM
UTF-8以字节为编码单元,没有字节序的问题。UTF-16以两个字节为编码单元,在解释一个UTF-16文本前,首先要弄清楚每个编码单元的字节序。例如收到一个“奎”的Unicode编码是594E,“乙”的Unicode编码是4E59。如果我们收到UTF-16字节流“594E”,那么这是“奎”还是 “乙”?
Unicode规范中推荐的标记字节顺序的方法是BOM。BOM不是“Bill Of Material”的BOM表,而是Byte Order Mark。BOM是一个有点小聪明的想法: 在 UCS编码中有一个叫做"ZERO WIDTH NO-BREAK SPACE"的字符,它的编码是FEFF。而FFFE在UCS中是不存在的字符,所以不应该出现在实际传输中。UCS规范建议我们在传输字节流前,先传输字符"ZERO WIDTH NO-BREAK SPACE"。
这样如果接收者收到FEFF,就表明这个字节流是Big-Endian的;如果收到FFFE,就表明这个字节流是Little-Endian的。因此字符"ZERO WIDTH NO-BREAK SPACE"又被称作BOM。
UTF-8不需要BOM来表明字节顺序,但可以用BOM来表明编码方式。字符"ZERO WIDTH NO-BREAK SPACE"的UTF-8编码是EF BB BF(读者可以用我们前面介绍的编码方法验证一下)。所以如果接收者收到以EF BB BF开头的字节流,就知道这是UTF-8编码了。
Windows就是使用BOM来标记文本文件的编码方式的。
6、进一步的参考资料
本文主要参考的资料是 "Short overview of ISO-IEC 10646 and Unicode"。
UNICODE与UTF8、UTF16
由于每种语言都制定了自己的字符集,导致最后存在的各种字符集实在太多,在国际交流中要经常转换字符集非常不便。因此,产生了Unicode字符集,它固定使用16bits两个字节来表示一个字符,共可以表示65536个字符, 标准的Unicode称为UTF-16(UTF:UCS Transformation Format )。用比较简单的话来说就是,Unicode定义了所有可以用来表示字符的数值集合称之为Code Point。UTF-8和UTF-16等UTF标准定义了这些数值和字符的映射关系。
后来为了双字节的Unicode能够在现存的处理单字节的系统上正确传输,出现了UTF-8,使用类似MBCS的方式对Unicode进行编码。毕竟互联网70%以上的信息仍然是英文。如果连英文都用2个字节存取(UCS-2),空间浪费不就太多了?所谓UTF-8就是这样一个为了提高英文存取效率的字符集转换格式:UnicodeTransformation Form 8-bit form。用UTF-8,UNICODE的2字节字符用变长个1-3个字节表示:
对英文,仍然和ASCII一样用1个字节表示,这个字节的值小于128(/x80);
对其他语言的用一个值位于128-256之间的字节开始,再加后面紧跟的2个字节表示,一个字符一共是3个字节;
当一个软件打开一个文本时,它要做的第一件事是决定这个文本究竟是使用哪种字符集的哪种编码保存的。软件一般采用三种方式来决定文本的字符集和编码:
检测文件头标识,提示用户选择,根据一定的规则猜测,最标准的途径是检测文本最开头的几个字节,开头字节 Charset/encoding,如下表:
EF BB BF UTF-8
FE FFUTF-16/UCS-2, little endian
FF FEUTF-16/UCS-2, big endian
FF FE 00 00 UTF-32/UCS-4, little endian
00 00 FE FF UTF-32/UCS-4, big-endian
Unicode的最初目标,是用1个16位的编码来为超过65000字符提供映射。但这还不够,它不能覆盖全部历史上的文字,也不能解决传输的问题(implantation head-ache's),尤其在那些基于网络的应用中。已有的软件必须做大量的工作来程序16位的数据。因此Unicode用一些基本的保留字符制定了三套编码方式。它们分别是UTF-8,UTF-16和UTF-32。正如名字所示,在UTF-8中,字符是以8位序列来编码的,用一个或几个字节来表示一个字符。这种方式的最大好处,是UTF-8保留了ASCII字符的编码做为它的一部分,例如在UTF-8和ASCII中,“A”的编码都是0x41。
UTF-16和UTF-32分别是Unicode的16位和32位编码方式。考虑到最初的目的,通常说的Unicode就是指UTF-16。在讨论Unicode时,搞清楚哪种编码方式非常重要。
UTF-8的好处是兼容性,毕竟在技术圈ASCII范围内的字符用的是最多的,原有的支持ASCII的程序很多不用太多修改就能支持UTF-8,而且对于主要是西文的内容来说存储空间利用率比较高。UTF-16最开始是用来配合UCS-2使用的,那时Unicode还只有不到65536个字符,只需2个字节就能搞定,Windows NT面对国际化的需求也采用UTF-16作为系统字符编码。
UTF-16 曾经是可以当定长编码用的,这也是当初会选择他们的主要原因。但是计划比不上变化快,Unicode收录的字符很快就超过了65536个。所以如果还想用定长编码似乎只能采取UTF-32这种编码方式了。可是这种方式最大的问题是即使是英文字母也要四个字节来存储,空间浪费太大了。所以UTF-8这种变长编码方式开始流行起来了,英文字母只需要一个字节,汉字三个字节。更古怪更稀有的字符可以用四个,五个或更多字节表示,因为使用频率低,所以空间浪费不大。当然定长编码的好处是可以快速定位字符,对于string.charAt(index)方法有着较好的支持。UTF-8的话,就需要从头开始一个字符一个字符的解析才行,会慢一点。但是与查询定位相比,顺序输出的情况更多,所以平常也不会感受到效率会比较慢。未来的趋势是UTF-8,文件编码是UTF-8,数据库编码是UTF-8,网络流编码是UTF-8,这样真的能减少很多麻烦,现在想要解决编码问题,统一UTF-8化是最佳解决方案。
UTF-16比较尴尬,它一来存储空间利用率不高,二来又是个变长编码无法直接访问其中的code point。
到底 Unicode 和 UTF-8 是什么关系?一句话:Unicode 是字符集;UTF-8 是编码。
UTF-8
优势
UTF-8最大的优势是,没有字节序的概念。所以特别适合用于字符串的网络数据传输,不用考虑大小端问题。
劣势
本地字符串处理过程中,如果使用UTF-8,对于英文字符的处理没有太大的问题。一个char变量表示一个英文字符。但是对于中文等远东字符集来说,就比较坑了。char str[]; str[0]并不能完整表示一个汉字。UTF-8编码格式下,一个汉字需要至少3个char才能表示。这对于通过下标来操作字符串的操作来说是非常痛苦的一件事情。
另外,一个汉字需要至少3个char来表示,也让汉字在网络传输上存在劣势,占用太多流量。
UTF-16
优势
UTF-16 LE是windows上默认的Unicode编码方式,使用wchar_t表示。所有wchar_t *类型的字符串(包括硬编码在.h/.cpp里的字符串字面值),VC都自动采用UTF-16的编码(字符串字面值,literal string,存在很多坑。特别是char *类型的字面值,最终内存使用何种编码方式完全取决于当前文件的编码方式。也就是说当前文件如果是GBK编码的,那么文件里char * str = "中午",str指向的内存字符串二进制是使用GBK编码的。如果文件编码是UTF-8,那么内存是使用UTF-8编码。所以为什么一直要强调字符串应该放在资源文件里,而不是硬编码在.h/.cpp文件里!)。
劣势
没有统一的表示UTF-16编码的字符类型。C++98/03里对wchar_t的定义是非常宽泛的。这导致在Windows平台上,wchar_t是2字节的;在Unix-like系统上是4字节的。代码移植上可能会遇到挑战。
UTF-16编码字符串的网络传输,要考虑大小端的问题。
UTF-8最适合用来作为字符串网络传输的编码格式。UTF-16最适合当作本地字符串编码格式。如果定义好了网络传输协议,那么UTF-16也非常合适当作网络字符串传输的编码格式,特别是中文等远东地区字符集。比起UTF-8来说,节省流量。
GB2312、GBK、GB18030 这几种字符集的主要区别是什么
GBK和GB2312 都是16位的,GBK支持简体中文和繁体中文,而GB2312只支持简体中文,GBK里面包含了GB2312,用GBK比较多。GB18030是32位的,它支持简体中文、繁体中文 藏文、蒙文、维吾尔文等主要的少数民族文字,包含GBK和GB2312。也就是说从GB2312(1980年)、GBK(1995年)到GB18030(2000年),这些编码方法是向下兼容的。
GBK是最新的国家汉字编码标准(GBK/GB13000),它兼容原来的GB2312-80。支持ISO/IEC 10646-1和GB13000-1的全部中日韩(CJK)汉字,共收汉字21003个,而GB2312-80只收了6763个汉字。专业用户使用GBK字库可以很好地解决排版书籍、报纸、杂志的生僻字问题,同时辞书、古籍、户籍的编辑管理等诸多领域也可通过GBK字库来大幅度地提高工作效率和质量。
2000年3月17日发布了GB18030汉字编码国家标准,全名是《信息技术汉字编码字符集基本集的扩充》,它是对GBK编码的扩充,覆盖中文、日文、朝鲜语和中国少数民族文字,其中收录27484个汉字。GB18030字符集采用单字节、双字节和四字节三种方式对字符编码。兼容GBK和GB2312字符集。GB18030-2000是全文强制性标准,市场上销售的产品必须符合。
GB18030最新版本是GB18030-2005,它在GB18030-2000的基础上增加了42711个汉字和多种我国少数民族文字的编码,增加的这些内容是推荐性的,收录了70244个汉字。原GB18030-2000中的内容是强制性的,市场上销售的产品必须符合。故GB18030-2005为部分强制性标准,自发布之日起代替GB18030-2000。GB18030-2005标准采用单字节、双字节和四字节三种方式对字符编码。
GB13000-2010目的是替换GB13000-1993标准。
1、GB2312-80
GB2312 或 GB2312-80 是中国国家标准简体中文字符集,全称《信息交换用汉字编码字符集·基本集》,又称 GB 0,由中国国家标准总局发布,1981 年 5 月 1 日实施。GB2312 编码通行于中国大陆;新加坡等地也采用此编码。中国大陆几乎所有的中文系统和国际化的软件都支持 GB2312。
GB2312 标准共收录 6763 个汉字,其中一级汉字 3755 个,二级汉字 3008 个;同时收录了包括拉丁字母、希腊字母、日文平假名及片假名字母、俄语西里尔字母在内的 682 个字符。
GB2312 的出现,基本满足了汉字的计算机处理需要,它所收录的汉字已经覆盖中国大陆99.75% 的使用频率。对于人名、古汉语等方面出现的罕用字,GB2312 不能处理,这导致了后来 GBK 及 GB18030 汉字字符集的出现。
GB2312 对任意一个图形字符都采用两个字节表示,并对所收汉字进行了“分区”处理,每区含有 94 个汉字/符号,分别对应第一字节和第二字节。这种表示方式也称为区位码:
01-09 区为特殊符号。
16-55 区为一级汉字,按拼音排序。
56-87 区为二级汉字,按部首/笔画排序。
10-15 区及 88-94 区则未有编码。
GB2312 的编码范围为 2121H-777EH,与 ASCII 有重叠,通行方法是将 GB 码两个字节的最高位置 1 以示区别。
GB2312-80 编码是中华人民共和国国家标准汉字信息交换用编码,全称《信息交换用汉字编码字符集基本集》标准号为GB2312-80(GB是“国标”二字的汉语拼音缩写),由中华人民共和国国家标准总局发布,1981年5与1号发布,1981年10月1日实施。它是一个简化字汉字的编码,通行于中国大陆地区及海外使用简体中文的地区(如新加坡等)。 GB2312-80 收录简化汉字及一般符号、序号、数字、拉丁字母、日文假名、希腊字母、俄文字母、汉语拼音符号、汉语注音字母、共7445个图形字符。其中汉字以外的图形字符682个,汉字6763个。 GB2312-80 把收录的汉字分成两级。第一级汉字是常用汉字,计3755个,置于16~55区,按汉语拼音字母/笔形顺序排列;第二级汉字是次常用汉字,计3008个,置于56~87区,按部首/笔画顺序排列。字音以普通话审音委员会发表的《普通话异读词三次审音总表初稿》(1963年出版)为准,字形以中华人民共和国文化部、中国文字改革委员会公布的《印刷通用汉字字形标》(1964年出版)为准。
2、GBK
GBK 即汉字内码扩展规范,K 为汉语拼音 Kuo Zhan(扩展)中“扩”字的声母。英文全称 Chinese Internal Code Specification。
GBK 共收入 21886 个汉字和图形符号,包括:
GB2312 中的全部汉字、非汉字符号。
BIG5 中的全部汉字。
与 ISO 10646 相应的国家标准 GB 13000 中的其它 CJK 汉字,以上合计 20902 个汉字。
其它汉字、部首、符号,共计 984 个。
GBK 向下与 GB2312 完全兼容,向上支持 ISO 10646 国际标准,在前者向后者过渡过程中起到的承上启下的作用。GBK 采用双字节表示,总体编码范围为 8140-FEFE 之间,首字节在 81-FE 之间,尾字节在 40-FE 之间,剔除 XX7F 一条线。GBK 编码区分三部分:
汉字区 包括
GBK/2:OXBOA1-F7FE, 收录 GB2312 汉字 6763 个,按原序排列;
GBK/3:OX8140-AOFE,收录 CJK 汉字 6080 个;
GBK/4:OXAA40-FEAO,收录 CJK 汉字和增补的汉字 8160 个。
图形符号区 包括
GBK/1:OXA1A1-A9FE,除 GB2312 的符号外,还增补了其它符号
GBK/5:OXA840-A9AO,扩除非汉字区。
用户自定义区
GBK 区域中的空白区,用户可以自己定义字符。
GBK 编码标准,全称《汉字内码扩展规范》(GBK),英文名称 Chinese Internal Code Specification,中华人民共和国全国信息技术标准化技术委员会1995年12月1日制定,国家技术监督局标准化司、电子工业部科技与质量监督司1995年12月15日联合以技监标函[1995]229号文件的形式,将它确定为技术规范指导性文件,发布和实施。这一版的GBK规范为1.0版。GB即“国标”,K是“扩展”的汉语拼音第一个字母。 GBK向下与GB2312编码兼容,向上支持 ISO 10646.1 国际标准,是前者向后者过度过程中的一个承上启下的标准。 GBK共收录21886个汉字和图形符号,其中汉字(包括部首和构件)21003个,图形符号883个。
3、GB18030
GB18030,全称:国家标准 GB18030-2005《信息技术中文编码字符集》,是中华人民共和国现时最新的内码字集,是 GB18030-2000《信息技术信息交换用汉字编码字符集基本集的扩充》的修订版。GB18030 与 GB2312-1980 和 GBK 兼容,共收录汉字70244个。
与 UTF-8 相同,采用多字节编码,每个字可以由 1 个、2 个或 4 个字节组成。
编码空间庞大,最多可定义 161 万个字符。
支持中国国内少数民族的文字,不需要动用造字区。
汉字收录范围包含繁体汉字以及日韩汉字
GB18030 编码是一二四字节变长编码。
单字节,其值从 0 到 0x7F,与 ASCII 编码兼容。
双字节,第一个字节的值从 0x81 到 0xFE,第二个字节的值从 0x40 到 0xFE(不包括0x7F),与 GBK 标准兼容。
四字节,第一个字节的值从 0x81 到 0xFE,第二个字节的值从 0x30 到 0x39,第三个字节从0x81 到 0xFE,第四个字节从 0x30 到 0x39。
GB18030有两个版本:GB18030-2000和GB18030-2005,GB18030-2000是GBK的取代版本,它的主要特点是在GBK的基础上增加了CJK统一汉字扩充A的汉字,GB18030-2005的主要特点是在GB18030-2000基础上增加了CJK统一汉字扩充B的汉字。GB18030-2000,全称《信息技术信息交换用汉字编码字符集基本集的扩充》,中华人民共和国信息产业部电子工业标准化研究所起草,由国家质量技术监督局于2000年3月17号发布。GB18030-2000共收录27533个汉字。
GB18030,全称:国家标准 GB18030-2005《信息技术中文编码字符集》,是中华人民共和国现在最新的内码字符集,是GB18030-2000的修订版。GB18030-2005 共收录汉字70244个。现行版本为国家质量监督检验总局和中国国家标准化管理委员会于2005年11月8日发布,2006年5月1日实施。此规格为在中国境内所有软件产品支持的强制规格。
再说说区位码、GB2312、内码和代码页
有的朋友对文章中这句话还有疑问: “GB2312的原文还是区位码,从区位码到内码,需要 在高字节和低字节上分别加上A0。”
再详细解释一下:“GB2312的原文”是指国家1980年的一个标准《中华人民共和国国家标准信息交换用汉字编码字符集基本集 GB2312-80》。这个标准用两个数来编码汉字和中文符号。第一个数称为“区”,第二个数称为“位”。所以也称为区位码。1-9区是中文符号,16-55 区是一 级汉字,56-87区是二级汉字。现在Windows也还有区位输入法,例如输入1601得到“啊”。
内码是指操作系统内部的字符编码。早期操作系统的内码是与语言相关的,现在的Windows在内部统一使用Unicode,然后用代码页适应各种语言,“内码”的概念就比较模糊了。微软一般将缺省代码页指定的编码说成是内码,在特殊的场合也会说自己的内码是Unicode, 例如在 GB18030问题的处理上。
所谓代码页(code page)就是针对一种语言文字的字符编码。例如GBK的code page是CP936,BIG5的code page是CP950,GB2312的code page是CP20936。
Windows中有缺省代码页的概念,即缺省用什么编码来解释字符。例如Windows的记事本打开了一个文本文件,里面的内容是字节流:BA、BA、D7、D6。Windows应该去怎么解释它呢?是按照Unicode编码解释、还是按照GBK解释、还是按照BIG5解释,还是按照ISO8859-1去解释?如果按GBK去解释,就会得到“汉字”两个字。按照其它编码解释,可能找不到对应的 字符,也可能找到错误的字符。所谓“错误”是指与文本作者的本意不符,这时就产生了乱码。
答案是Windows按照当前的缺省代码页去解释文本文件里的字节流。缺省代码页可以通过控制面板的区域选项设置。记事本的另存为中有一项ANSI,其实就是按照缺省代码页的编码方法保存。Windows的内码是Unicode,它在技术上可以同时支持多个代码页。只要文件能说明自己使用什么编码,用户又安装了对应的代码页,Windows就能正确显示,例如在HTML文件中就可以指定charset。
有的HTML文件作者,特别是英文作者,认为世界上所有人都使用英文,在文件中不指定charset。如果他使用了0x80-0xff之间的字符,中文Windows又按照缺省的GBK去解释,就会 出现乱码。这时只要在这个html文件中加上指定charset的语句,例如:如果原作者使用的代码页和ISO8859-1兼容,就不会出现乱码了。再说区位码,啊的区位码是1601,写成16进制是0x10,0x01。这和计算机广泛使用的ASCII编码冲突。为了兼容00-7f的ASCII编码,在区位码的高、低字节上分别加上A0。这样“啊”的编码就成为B0A1。将加过两个A0的编码也称为GB2312编码,虽然GB2312的原文根本没提到这一点。
cp936 与 gbk
cp936 就是 gbk,因为 IBM 发明代码页时,gbk 在代码页的第 936 页。相关的关键字如下:
NLS,cp936,GBK
NLS(Native Language System)
cp - code page
GB - guo biao 国标
GBK - guo biao kuo 国标扩展 - cp936
ANSI - 表示默认
Windows下在“控制面板”-“区域和语言选项”-“高级”-“代码页转换表”可以看出“936 (ANSI/OEM - 简体中文 GBK)”
cp936 Code Page 936, also known as GBK (Extended GuoBiao)
euc-cn /\beuc.*cn$/i EUC (Extended Unix Character) /\bcn.*euc$/i
在win10下的记事本已经是按UTF8的编码格式来保存文本文件了,在“另存为”的对话框中已经无法找到GBK了。
GB18030 中文编码字符集时隔多年再迎新版
2022年7月28日消息,国家市场监督管理总局国家标准化管理委员会2022年7月19日发布关于批准发布《信息技术中文编码字符集》等两项强制性国家标准的公告。该标准是中文信息技术领域最重要的基础性标准,需要进行中文处理的信息系统均需应用此类编码标准,用于将中文字符转换为计算机中对应的码,比如“蔷”对应的码为“C7BE”。该编码字符集对 GB 2312-1980 完全向后兼容,与 GBK 基本向后兼容,并支持 Unicode(GB13000)的所有码位。
全国标准信息公共服务平台显示,国家标准《信息技术中文编码字符集》由339(工业和信息化部)归口上报及执行,主管部门为工业和信息化部。主要起草单位包括中国电子技术标准化研究院、北京北大方正电子有限公司、北京亚细亚智业科技有限公司、潍坊北大青鸟华光照排有限公司、微软(中国)有限公司。首版于 2000 年发布,并在 2005 年进行了第一次修订。时隔 17 年再次迎来修订。新版的《信息技术中文编码字符集》强制性国家标准号为 GB18030-2022,将于2023年8月1日正式实施。GB18030-2022 共收录汉字 87887 个,比上一版增加录入了 1.7 万余个生僻汉字。