zoukankan      html  css  js  c++  java
  • 汉字编码问题

    程序员在日常的编程过程中,经常会遇到各种各样跟编码相关的问题。解决问题的根本当然是要弄清楚各个编码的来历以及具体的实现细节。

    Unicode

    在Unicode里,所有的字符被一视同仁,汉字不再使用“两个扩展ASCII”,而是使用“1个Unicode”来表示,也就是说,所有的文字都按一个字符来处理,它们都有一个唯一的Unicode码。Unicode用数字0-0x10FFFF来映射这些字符,最多可以容纳1114112个字符,或者说有1114112个码位(码位就是可以分配给字符的数字)。

    在Unicode中:汉字“字”对应的数字是23383(0x5b57)。在Unicode中,我们有很多方式将数字23383表示成程序中的数据,包括:UTF-8、UTF-16、UTF-32。UTF是“UCS Transformation Format”的缩写,可以翻译成Unicode字符集转换格式,即怎样将Unicode定义的数字转换成程序数据。例如,“汉字”对应的数字是0x6c49和0x5b57,而编码的程序数据是:

    1
    2
    3
    BYTEdata_utf8[] = {0xE6, 0xB1, 0x89, 0xE5, 0xAD, 0x97}; // UTF-8编码
    WORDdata_utf16[] = {0x6c49, 0x5b57}; // UTF-16编码
    DWORDdata_utf32[] = {0x6c49, 0x5b57}; // UTF-32编码

    这里用BYTE、WORD、DWORD分别表示无符号8位整数,无符号16位整数和无符号32位整数。UTF-8、UTF-16、UTF-32分别以BYTE、WORD、DWORD作为编码单位。“汉字”的UTF-8编码需要6个字节。“汉字”的UTF-16编码需要两个WORD,大小是4个字节。“汉字”的UTF-32编码需要两个DWORD,大小是8个字节。

    UTF-8与Unicode的关系

            UTF是为传输而设计的编码,其系列还有UTF-7和UTF-16,其中UTF-16和Unicode编码大致一样, UTF-8就是以8位(一个字节)为单元对Unicode进行编码。从Unicode到UTF-8的编码方式如下:

    Unicode编码(16进制) UTF-8 字节流(二进制)
    000000 – 00007F 0xxxxxxx
    000080 – 0007FF 110xxxxx 10xxxxxx
    000800 – 00FFFF 1110xxxx 10xxxxxx 10xxxxxx
    010000 – 10FFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

     

    UTF-8的特点是对不同范围的字符使用不同长度的编码。对于0×00-0x7F之间的字符,UTF-8编码与ASCII编码完全相同。UTF-8编码的最大长度是4个字节。从上表可以看出,4字节模板有21个x,即可以容纳21位二进制数字。Unicode的最大码位0x10FFFF也只有21位。总结了一下规律:UTF-8的第一个字节开始的1的个数代表了总的编码字节数,后续字节都是以10开始。由上面的规则可以清晰的看出UTF-8编码克服了中文编码的两个问题。

    “汉”字的Unicode编码是0x6C49。0x6C49在0×0800-0xFFFF之间,使用3字节模板了:1110xxxx 10xxxxxx 10xxxxxx。将0x6C49写成二进制是:0110 1100 0100 1001, 用这个比特流依次代替模板中的x,得到:11100110 10110001 10001001,即E6 B1 89。

    字节序

    顾名思义,“字节序”就是字节的顺序,也即大于一个字节类型的数据在内存中存放的顺序。UTF-8是以单字节为单位的,所以不涉及字节序的问题。而UTF-16可以被实现为UTF-16LE(Little Endian)或UTF-16BE(Big Endian),UTF-32可以被实现为UTF-32LE或UTF-32BE。

    那么,怎么判断字节流的字节序呢?Unicode标准建议用BOM(Byte Order Mark)来区分字节序,即在传输字节流前,先传输被作为BOM的字符”零宽无中断空格”。这个字符的编码是FEFF,而反过来的FFFE(UTF-16)和FFFE0000(UTF-32)在Unicode中都是未定义的码位,不应该出现在实际传输中。下表是各种UTF编码的BOM:

    UTF编码 Byte Order Mark
    UTF-8 EF BB BF
    UTF-16LE FF FE
    UTF-16BE FE FF
    UTF-32LE FF FE 00 00
    UTF-32BE 00 00 FE FF

     

    总结一下,ISO与unicode.org都敏锐的意识到只有为世界上每种语言中的每个字符设定统一并且唯一的二进制编码才能彻底解决计算机世界信息交流中编码冲突的问题。由此诞生了UCS和unicode,而这两个规范是一致的。在Unicode里,所有的字符被一视同仁,也就是说,所有的文字都按一个字符来处理,它们都有一个唯一的Unicode码。UTF-8、UTF-16、UTF-32分别定义了怎样将Unicode定义的数字转换成程序数据。UTF-8以字节为单位对Unicode进行编码,一个英文字符占1个字节,汉字占3个字节;UTF-16以16位无符号整数为单位对Unicode进行编码,中文英文都占2个WORD;UTF-32以32位无符号整数为单位对Unicode进行编码,中文英文都占2个DWORD。

     

    顺便记一下其它的汉字编码:

    GB2312

    GB2312码即中华人民共和国国家汉字信息交换用编码,全称《信息交换用汉字编码字符集——基本集》,1981年5月1日实施,通行于大陆。GB2312字符集中除常用简体汉字字符外还包括希腊字母、日文平假名及片假名字母、俄语西里尔字母等字符,未收录繁体中文汉字和一些生僻字。 EUC-CN可以理解为GB2312的别名,和GB2312完全相同。

    GB2312是基于区位码设计的,在区位码的区号和位号上分别加上A0H就得到了GB2312编码。这里的A0H其实包含两个部分,用于产生国标码的20H和用于区分于ASCII的80H(20H + 80H = AH),具体细节如下:

     

    中国国家标准总局把中文常用字符编码为94个区,每个区对应94个位,每个字符的区号和位号组合起来就是该字符的区位码, 区位码用10进制数来表示,如4907就表示49区7位,对应的字符是“学”。

    由于区位码的取值范围与通信使用的控制码(00H~1FH)(即ASCII码特殊字0~31)发生冲突。于是ISO2022规定每个汉字的区号和位号分别加上32(即16进制20H)得到对应的国标交换码,简称国标码,交换码。

    无论什么编码,在计算机系统中使用时都要经过二进制转换,这就是系统内码“学”的国标码为5127H。由于文本中通常混合使用汉字和西文字符,为了让汉字信息不会与单字节的ASCII码混淆,将一个汉字看成是两个扩展ASCII码,即汉字的两个字节的最高位置为1,得到的编码为GB2312汉字的内码

    “学”的内码为D1A7H。无论你使用什么输入法,通过什么样的按键组合把“学”输入计算机,“学”在使用GB2312(以及兼容GB2312)编码的计算机里的内码都是D1A7H。

    GBK

    GB2312的出现基本满足了汉字的计算机处理需要,但由于上面提到未收录繁体字和生僻字,从而不能处理人名、古汉语等方面出现的罕用字,这导致了1995年《汉字编码扩展规范》(GBK)的出现。GBK编码是GB2312编码的超集,向下完全兼容GB2312,兼容的含义是不仅字符兼容,而且相同字符的编码也相同,同时在字汇一级支持ISO/IEC10646—1和GB 13000—1的全部中、日、韩(CJK)汉字,共计20902字。GBK还收录了GB2312不包含的汉字部首符号、竖排标点符号等字符。CP936和GBK的有些许差别,绝大多数情况下可以把CP936当作GBK的别名。

    GB18030

    GB18030编码向下兼容GBK和GB2312。GB18030收录了所有Unicode3.1中的字符,包括中国少数民族字符,GBK不支持的韩文字符等等,也可以说是世界大多民族的文字符号都被收录在内。GBK和GB2312都是双字节等宽编码,如果算上和ASCII兼容所支持的单字节,也可以理解为是单字节和双字节混合的变长编码。GB18030编码是变长编码,有单字节、双字节和四字节三种方式。

    以上总结

    这些编码的共性是变长编码,单字节ASCII兼容,对其他字符GB2312和GBK都使用双字节等宽编码,只有GB18030还有四字节编码的方式。这些编码最大的问题是2个。1.由于低字节的编码范围和ASCII有重合,所以不能根据一个字节的内容判断是中文的一部分还是一个独立的英文字符。2.如果有两个汉字编码为A1A2B1B2,存在A2B1也是一个有效汉字编码的特殊情况。这样就不能直接使用标准的字符串匹配函数来判断一个字符串里是否包含某一个汉字,而需要先判断字符边界然后才能进行字符匹配判断。

    上面讲的都是大陆推行的汉字编码标准,使用繁体的中文社群中最常用的电脑汉字字符集标准叫大五码(Big5),共收录13,060个中文字,其中有二字为重覆编码。

    使用 2 个字节来代表一个字符的各种汉字延伸编码方式,称为 ANSI 编码。在简体中文系统下,ANSI 编码代表 GB2312 编码,在日文操作系统下,ANSI 编码代表 JIS 编码。

  • 相关阅读:
    OpenWrt/LEDE 没有slabtop命令
    OpenWrt/LEDE 没有/proc/slabinfo文件
    泰坦陨落2 origin安装时vc++runtime没有安装成功错误
    OpenWrt/LEDE中使用qt库
    椭圆曲线加密(ECC):域和离散对数
    batman-adv使用中修改一跳惩罚,batctl无法修改hop_penalty
    VMnet8设置ping通外网
    支持自动水平拆分的高性能分布式数据库TDSQL
    cronsun是替换 crontab 一个不错的选择
    MySQL 的 20+ 条最佳实践
  • 原文地址:https://www.cnblogs.com/worldtraveler/p/2876406.html
Copyright © 2011-2022 走看看