美文网首页
Unicode字符集与编码

Unicode字符集与编码

作者: buzzerrookie | 来源:发表于2019-05-26 13:35 被阅读0次

    本文简要介绍Unicode的相关知识,以澄清部分概念。

    Unicode字符集

    Unicode是一个字符集,它是所有其他广泛使用字符集的超集,包含了来自ISO/IEC 6937、ISO/IEC 8859家族、ANSI Z39.64、KS X 1001、JIS X 0208、JIS X 0212、JIS X 0213、GB 2312、GB 18030、HKSCS和CNS 11643等字符集的字符。
    Unicode标准完全遵守国际标准ISO/IEC 10646:2017, Information Technology—Universal Coded Character Set (UCS)。Unicode 1.0版于1991年发布,2.0版于1996年发布,写作本文时Unicode的最新版本是12.0,其他各版本的发布历史可参考History of Unicode Release

    术语定义

    Unicode中的常用术语如下:

    • 代码点(code point):Unicode中的每个数值都对应一个字符,这个数值就叫做代码点,通常使用十六进制表示,以“U+”开头,如U+0041表示英文字符A;
    • 补充代码点:位于U+10000..U+10FFFF之间的代码点,对应的字符叫做补充字符。Unicode 2.0引入了补充字符,但直到3.1才为补充字符赋值;
    • 代码空间(code space):代码点的合法范围,0至10FFFF16,即有1,114,112个代码点;
    • 代码单元(code unit):能表示一个单元的编码文本的最小位的组合;
    • 平面(plane):一个平面是65,536(1000016)个连续的Unicode代码点,第一个代码点是65536的整数倍。平面的编号从0至16,所以平面0包括U+0000..U+FFFF,平面1包括U+10000..U+1FFFF,…,平面16包括U+100000..U+10FFFF。平面0叫做基本多语言平面(BMP),其余平面叫做补充平面;
    • 属性(property):字符的性质,如大写还是小写,是否是数字;
    • 字母表(script):书写系统中字母和其他书写符号的集合,如俄语是西里尔字母表的子集;
    • 区块(block):Unicode中的一组字符,如Tibetan区块包含U+0F00..U+0FFF共256个代码点。

    组合字符

    Unicode中的每个代码点都对应一个字符,但是一个字符可以对应多个代码点,代码点与字符并不是一一映射。
    以字符Á为例,它有两种表示方法:

    • 单个代码点U+00C1;
    • 两个代码点U+0041(A)与U+0301( ́)一起表示。

    而对字符Ệ,则有三种表示方法:

    • 单个代码点U+1EC6;
    • 三个代码点U+0045(E)、U+0323( ̣)和U+0302( ̂)一起表示;
    • 三个代码点U+0045(E)、U+0302( ̂)和U+0323( ̣)一起表示,注意与第二种顺序不同。

    Unicode提供了许多组合字符(Combining Character或Combining Mark)修饰基本字符,正如Ệ所示,多个组合字符的顺序也可以不同。为什么会出现这样的情况呢?这是为了保证Unicode与其他字符集之间转换的简易型,如ISO 8859-1/2/3/4/9/10/14/15/16中Á的编码是C1,Unicode就使用单个代码点U+00C1表示该字符。

    归一化

    组合字符为字符比较带来了问题,因为看着一样的字符实际可能是由不同代码点表示的,Unicode Standard Annex #15定义了两种相等:

    • 规范相等(canonical equivalence):规范相等是一种字符或者序列间的基本相等形式,它们表示相同的抽象字符,当正确显示时总是应该有相同的外观或行为,如Ç与C和◌̧的组合;
    • 兼容相等(compatibility equivalence):兼容相等则是一种较弱的相等形式,字符或者序列表示相同的抽象字符,但可以有不同的外观或行为。如ℌ与H。

    Normalization FAQ指出程序总是应该使用规范相等执行比较相等的操作,最简单的方法就是归一化字符串:如果字符串被转换成了归一化形式,则规范相等的串会有完全相同的二进制表示。为了使用规范相等,可以使用Unicode标准提供的NFC与NFD归一化形式。Unicode标准一共定义了四种归一化(normalization)形式:

    • Normalization Form D (NFD):规范分解;
    • Normalization Form C (NFC):规范分解,接着规范组合,一般使用NFC;
    • Normalization Form KD (NFKD):兼容分解;
    • Normalization Form KC (NFKC):兼容分解,接着兼容组合。

    组合字符对正则表达式的影响:如点号是匹配单个代码点,还是由基本字符和组合字符组成的整个代码点序列?在实践中,许多程序的点号匹配单个代码点,无论它代表基本字符还是组合字符,即Ệ(U+0045、U+0302和U+0323)由三个点号而不是一个点匹配。

    Unicode编码形式

    Unicode标准支持三种编码形式(encoding form),分别是UTF-32、UTF-16和UTF-8。UTF(Unicode Transformation Format)编码是一种从Unicode代码点到唯一字节序列的映射,Unicode标准第3.9节是UTF的正式定义。

    UTF-32

    UTF-32编码形式使用32位无符号代码单元表示Unicode代码点:

    • 位于0000D80016..0000DFFF16间的的代码单元是非法的;
    • 大于0010FFFF16的代码单元是非法的;
    • 以代码点序列<004D, 0430, 4E8C, 10302>为例,它会被编码成<0000004D 00000430 00004E8C 00010302>。

    UTF-16

    UTF-16编码形式使用单个16位无符号代码单元表示位于U+0000..U+D7FF和U+E000..U+FFFF之间的代码点,使用替代对表示U+10000..U+10FFFF之间的代码点:

    • 为了使UTF-16能表示U+10000以上的代码点,Unicode引入了替代(surrogate)代码点,高位替代代码点位于U+D800..U+DBFF,低位替代代码点位于U+DC00..U+DFFF,一个替代对由一个高位替代代码点和一个低位替代代码点组成;
    • 独立的位于D80016..DFFF16间的的代码单元是非法的;
    • 以代码点序列<004D, 0430, 4E8C, 10302>为例,它会被编码成<004D 0430 4E8C D800 DF02>,其中<D800 DF02>对应U+10302。
    代码点 代码点二进制表示 UTF-16
    U+0000..U+D7FF和U+E000..U+FFFF xxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxx
    U+10000..U+10FFFF 000uuuuuxxxxxxxxxxxxxxxx 110110wwwwxxxxxx 110111xxxxxxxxxx

    其中wwww = uuuuu - 1

    UTF-8:

    UTF-8编码形式使用1到4个无符号字节序列表示Unicode代码点:

    • 以代码点序列<004D, 0430, 4E8C, 10302>为例,它会被编码成<4D D0 B0 E4 BA 8C F0 90 8C 82>,其中<4D>对应U+004D,<D0 B0>对应U+0430,<E4 BA 8C>对应U+4E8C,<F0 90 8C 82>对应U+10302。
    代码点 第1个字节 第2个字节 第3个字节 第4个字节
    00000000 0xxxxxxx 0xxxxxxx
    00000yyy yyxxxxxx 110yyyyy 10xxxxxx
    zzzzyyyy yyxxxxxx 1110zzzz 10yyyyyy 10xxxxxx
    000uuuuu zzzzyyyy yyxxxxxx 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx

    Unicode编码方案

    Unicode编码方案(encoding scheme)是指Unicode编码形式的一种特定字节序列化,包括对字节顺序标记(BOM)的处理。Unicode标准支持七种编码方案:

    • UTF-8编码方案:以与代码单元完全相同的顺序序列化UTF-8代码单元,字节序列<EF BB BF>可以被用作UTF-8的BOM,它与字节序无关,只是表明UTF-8编码;
    • UTF-16BE编码方案:以大尾端形式序列化UTF-16代码单元,字节序列<FE FF>被用作UTF-16BE的BOM;
    • UTF-16LE编码方案:以小尾端形式序列化UTF-16代码单元,字节序列<FF FE>被用作UTF-16LE的BOM;
    • UTF-16编码方案:以大尾端或小尾端形式序列化UTF-16代码单元,检查初始字节序列,<FE FF>表明是大尾端,而<FF FE>表明是小尾端,否则若均不是则默认是大尾端;
    • UTF-32BE编码方案:以大尾端形式序列化UTF-32代码单元,字节序列<00 00 FE FF>被用作UTF-32BE的BOM;
    • UTF-32LE编码方案:以小尾端形式序列化UTF-32代码单元,字节序列<FF FE 00 00>被用作UTF-32LE的BOM;
    • UTF-32编码方案:以大尾端或小尾端形式序列化UTF-32代码单元,检查初始字节序列,<00 00 FE FF>表明是大尾端,而<FF FE 00 00>表明是小尾端,否则若均不是则默认是大尾端。

    以U+004D为例,三种编码形式的代码单元如下:

    UTF-8代码单元 UTF-16代码单元 UTF-32代码单元
    4D 004D 0000004D

    五种编码方案如下:

    UTF-8 UTF-16BE UTF-16LE UTF-32BE UTF-32LE
    4D 00 4D 4D 00 00 00 00 4D 4D 00 00 00

    实现

    不同的编程语言对Unicode的支持程度不同。

    Java

    Java语言规范指出Java使用UTF-16编码表示文本序列。各JDK版本与遵守的Unicode版本对应关系如下:

    JDK版本 遵守的Unicode版本
    1.1之前 1.1.5
    1.1 2.0
    1.1.7 2.1
    1.4 3.0
    5.0 4.0
    7 6.0
    8 6.2
    9 8.0
    11 10.0
    12 11.0

    java.text包的Normalizer类的normalize方法提供了归一化功能,支持全部四种归一化形式。

    实例

    UTF-8

    U+0430
    00000yyy yyxxxxxx
    00000100 00110000
    
    11010000
    10110000
    
    U+4E8C
    zzzzyyyy yyxxxxxx
    01001110 10001100
    
    11100100
    10111010
    10001100
    
    U+10302
    000uuuuu zzzzyyyy yyxxxxxx
    00000001 00000011 00000010
    
    11110000
    10010000
    10001100
    10000010
    

    参考文献

    https://www.unicode.org
    http://www.oracle.com/us/technologies/java/supplementary-142654.html
    http://reedbeta.com/blog/programmers-intro-to-unicode/
    https://www.infoq.com/presentations/unicode-history
    精通正则表达式

    相关文章

      网友评论

          本文标题:Unicode字符集与编码

          本文链接:https://www.haomeiwen.com/subject/mqmmwqtx.html