美文网首页
Unicode和UTF-8、UTF-16、UTF-32

Unicode和UTF-8、UTF-16、UTF-32

作者: 凌云壮志几多愁 | 来源:发表于2017-03-02 19:09 被阅读1902次

    写在前面

    如果你是iOS开发者,并且在处理NSString字符上遇到了一些问题,强烈建议去看看Objc中国上关于 NSString 与 Unicode

    简介

    Unicode对世界上大部分的文字系统进行了整理、编码,使得电脑可以用更为简单的方式来呈现和处理文字。这是维基百科对Unicode下的定义。
    Unicode的实现方式包含了UTF-8、UTF-16(字符用两个字节或者四个字节表示)和UTF-32(用四个字节来表示),下面对面一一进行介绍。

    UTF-8

    UTF-8的最明显的一个特点是它是变长的,它可以使用1到4个字节表示一个符号,根据不同的符号变化字节长度
    先把阮一峰在《字符编码笔记:ASCII,Unicode和UTF-8》中对UTF-8的编写规则的一个总结放出来。

    ⚠️⚠️⚠️UTF-8的编码规则很简单,只有二条:
    1、对于单字节的符号,字节的第一位设为0,后面7位为这个符号的unicode码。因此对于英语字母,UTF-8编码和ASCII码是相同的。
    2、对于n字节的符号(n>1),第一个字节的前n位都设为1,第n+1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的unicode码。

    提出问题:第一个字节前面n位设1是为了知道当前字符占用多少字节,而后面字节的前两位字节为什么要设置为10呢?下面会马上进行解释。
    现在我们来具体的分析一下Unicode的不同范围:下面中前两个描述是否为ASCII,后两个描述多字节序列

    • U+0000到U+007F(ASCII)
      从U+0000到U+007F被编码为0x00~0x7F的单字节,这是ASCII码的所有字符,一共128个字符,所以Unicode是完全用来容纳ASCII的。

      回答上面提出的问题:后面字节的前两位一律设为10(10000000也就是80)是因为必须要大于7F才和ASCII码分开。

    • 大于 U+007F(非 ASCII)
      所有大于 U+007F 的字符被编码为一串多字节序列,这样就可以区分一串多字节序列是多字节码还是 ASCII 码。

    • 0xFE 和 0xFF 不会被用于 UTF-8 编码中。

    • 多字节序列的第一个字节在0xC00xFD中,剩余字节在0x800xBF内。
      这里解释一下为什么第一个是在0xC0~0xFD中,理解这里需要再回去看看上面注意中提到的Unicode编码规则。因为表示的是多字节就表明n是大于1的,所以第一个字节最小的值为:11000000即C0(表明当前有两个字节。每四位表示一个十六进制数,这也是为什么在编程的时候喜欢用十六进制数的原因),如果在没有限制的情况下,通过上面的结论我们可以得到第一个字节能表示的最大的数是0xFE(11111110),就是前面7位设置1最后一位设置为0,但是上面一条中提到不包含FE,所以第一个字节的最大值为0xFD(11111101)
      同理因为后面字节的前两位一律设为10所以多字节除了第一个字节的其他字节最大值为10111111(BF)

    总结
    UTF-8 编码字符最长可达六个字节

    Unicode 字符:             UTF-8 码:
    U-00000000 - U-0000007F:    0xxxxxxx      ///表示ASCII
    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
    

    举个例子:汉字“王”的Unicode码为U+73B8转换为二进制为:0111 0011 1010 1000,"73b8"位于上面的第三类,把转换的16个二进制依次放入(一定是一次放入不管空格的)上诉的x中:

    11100111 10001110 10101000
    /// 同样我们来验证一下阮一峰举例的“严”字
    /// 4E25 同样属于上诉的第三类 ,对应的二进制 => 0100 1110 0010 0101
    /// 11100100 10111000 10100101 得到的结果和他文章中的一样。
    

    到这里我自己觉得应该是把UTF-8的编码方式说清楚了,最后再来一个编码的顺序(很适合于我的方式)

    编码的顺序
    对于单字节:
    直接将其转换为八位的二进制就可以了;
    对于多字节:

    • 1.找到Unicode码对应的二进制数据
    • 2.查看该Unicode码在分类中属于第几类
    • 3.一次填入二进制码

    UTF-16

    UTF-16Unicode字符编码五层次模型的第三层:字符编码表(Character Encoding Form,也称为"storage format")的一种实现方式。即把Unicode字符集的抽象码位映射为16位长的整数(即码元)的序列,用于数据存储或传递。
    这里需要说明一下基本多文种平面-BMP辅助平面-SMP,在维基百科中每一个平面相关的图片下面都说了"每个写着数字的格子代表256个码点",即00~FF。例如:位于BMP中00格子中的一个码点表示为:0x00E5
    下面同样用一下阮一峰的规则总结:

    基本平面的字符占用2个字节,辅助平面的字符占用4个字节。也就是说,UTF-16的编码长度要么是2个字节(U+0000到U+FFFF),要么是4个字节(U+010000到U+10FFFF)。

    为了能够区分它本身是一个字符,还是需要跟其他两个字节放在一起解读。在BMP中,从U+D800U+DFFF之间BMP的区段是永久保留不映射到字符(从维基百科的图中D8~DF之间表示unallocated code points)。

    UTF-16结论(D800~DFFF)

    具体来说,辅助平面的字符位共有pow(2,20)个,也就是说,对应这些字符至少需要20个二进制位。UTF-16将这20位拆成两半,前10位映射在U+D800到U+DBFF(空间大小pow(2,10)),称为高位(H),后10位映射在U+DC00到U+DFFF(空间大小pow(2,10)),称为低位(L)。这意味着,一个辅助平面的字符,被拆成两个基本平面的字符表示。

    HHHH HHHH HHLL LLLL LLLL
    

    🌈🔥🔥🔥注意-结论
    高位:D800~DBFF;
    低位:DC00~DFFF;

    所以,当我们遇到两个字节,发现它的码点在U+D800到U+DBFF之间,就可以断定,紧跟在后面的两个字节的码点,应该在U+DC00到U+DFFF之间,这四个字节必须放在一起解读。(而不会拆成两个字节来读)

    解释一下这里为什么是pow(2,20),在基本平面之外有16个辅助平面(即pow(2,4)),而每一个辅助平面pow(2,16)个码位(辅助平面和基本平面一样,每个码位里面都包含了256个码点)。

    ///辅助平面字符,转码公式。
    /// js代码
    H = Math.floor((c-0x10000) / 0x400)+0xD800
    L = (c - 0x10000) % 0x400 + 0xDC00
    

    H为上文提到的高位,L位上文提到的低位。
    举例说明一下:

    /// 对于小于0xFFFF的即基本平面的字符,为两个字节
    U+8D9E = 0x8D9E  ///对应的二进制格式为:10001101 10011110
    
    /// 对出于辅助平面的字符
    /// 对于U+1D306
    H = Math.floor((0x1D306-0x10000) / 0x400)+0xD800 = d834
    L = (0x1D306 - 0x10000) % 0x400 + 0xDC00 = df06
    

    UTF-32

    因为UTF-32对每个字符都使用4字节,就空间而言,是非常没有效率的。特别地,非基本多文种平面的字符在大部分文件中通常很罕见,以致于它们通常被认为不存在占用空间大小的讨论,使得UTF-32通常会是其它编码的二到四倍。

    结论

    所以当我们在使用字符串的时候,通常使用length的时候,要看他的编码方式,并不是一个字符就代表了一个字节有可能是两个字节、四个字节甚至可能最多能到6个字节都是有可能的,这应该就能理解Swift中对于字符串的处理了。

    参考文献

    Objc中国上关于 NSString 与 Unicode
    Unicode代码图表
    字符编码笔记:ASCII,Unicode和UTF-8
    什么是UTF-8
    Unicode与JavaScript详解
    Unicode编码及其实现:UTF-16、UTF-8,and more
    UTF-16
    UTF-32

    相关文章

      网友评论

          本文标题:Unicode和UTF-8、UTF-16、UTF-32

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