Unicode 编码理解 以你之姓@ 2022-05-26 11:35 311阅读 0赞 Unicode(统一码、万国码、单一码)于1990年开始研发,1994年正式公布,是计算机领域里一项业界标准,包括字符集,编码方案等。Unicode是为了解决传统字符编码方案的局限而产生的,为每种语言中的每个字符都设定了统一唯一的二进制编码,以实现跨语言、跨平台进行文本转换、处理的要求。 计算机在设计时采用8个比特(bit)作为一个字节(byte),所以一个字节最多能表示256个字符,早期对于使用英文的西方国家来说,一个字节可以存储大小写英文字母、数学和一些符号,因此使用一个字节来制作码表(ASCII)。后来计算机传到了其他的国家,很多国家都是使用自己的语言,比如中文、日文、韩文…语言复杂了,为了解决这个问题,每个国家制定自己的码表,中国在1980年便制定了[GB2312][]汉字编码字符集,汉字比英文多很多,一个字节明显不够用,所有就使用2个字节来编码。然而不同国家所定义的字符编码虽然可以使用,但是在不同的国家间却经常出现不兼容的情况。如果电脑想处理多语言环境(使用中文或其他语言)可能存在无法同时支持多语言环境。 为了统一所有文字的编码,产生了Unicode,把所有语言的都统一到一套编码里,这样就不会乱码了。 在表示Unicode字符时,通常会用U+然后紧跟一组16进制的数字表示一个字符,在基本多文种平面(第零平面)Basic MultilingualPlane(BMP)里所有的字符都使用4位16进制表示。编码从U+0000到U+FFFF,共支持6万多个字符,在BMP以外的字符则需要使用5位或者6位16进制来表示。 目前Unicode字符分为17组编排,0x0000至0x10FFFF,每组称为平面(Plane),每个面拥有65536个码位,共1114112个。 Unicode就像一张表,包所有的字符都编写到表中,每一个字符对应一个数字,称为码点(code point),这个数字一般不直接使用,通过不同的编码方式来使用。 UTF-8、UTF-16、UTF-32就是将数字转换到程序数据的编码方案。UTF是“UnicodeTransformation Format”的缩写,可以翻译成Unicode字符集转换格式,即怎样将Unicode定义的数字转换成程序数据。 **UTF-8** UTF-8是以字节位单位对Unicode编码,第0-127位字符使用1个字节来表示,和ASCII编码相同,从128号开始的字符使用2、3、4位字节来表,UTF-8又被称为可变长编码。 UTF-8编码中,使用的是变成的字节序列表示字符,字符对应的代码点(code point)可能使用的是1-4个字节,这样一个字节就是一个代码单元。一个代码点(code point)可能由1-4个代码单元(code unit)组成。 <table> <tbody> <tr> <td><p><span style="color:rgb(51,51,51);">十进制</span></p></td> <td><p><span style="background:#F9FBFC;">Unicode</span><span style="background:#F9FBFC;">编码</span></p></td> <td><p><span style="background:#F9FBFC;">UTF-8</span><span style="background:#F9FBFC;">字节流</span></p></td> </tr> <tr> <td><p><span style="background:#F9FBFC;">0-127</span><span style="background:#F9FBFC;">位</span></p></td> <td><p><span style="background:#F9FBFC;">0x000000-0x00007F</span></p></td> <td><p><span style="background:#F9FBFC;">0xxxxxxx(7</span><span style="background:#F9FBFC;">位)</span></p></td> </tr> <tr> <td><p><span style="background:#F9FBFC;">128-2047</span><span style="background:#F9FBFC;">位</span></p></td> <td><p><span style="background:#F9FBFC;">0x000080-0x0007FF</span></p></td> <td><p><span style="background:#F9FBFC;">110xxxxx 10xxxxxx</span><span style="background:#F9FBFC;">(11</span>位)</p></td> </tr> <tr> <td><p><span style="background:#F9FBFC;">2048-65535</span><span style="background:#F9FBFC;">位</span></p></td> <td><p><span style="background:#F9FBFC;">0x000800-0x00FFFF</span></p></td> <td><p><span style="background:#F9FBFC;">1110xxxx 10xxxxxx 10xxxxxx(16</span><span style="background:#F9FBFC;">位)</span></p></td> </tr> <tr> <td><p><span style="background:#F9FBFC;">65536-1114111</span><span style="background:#F9FBFC;">位</span></p></td> <td><p><span style="background:#F9FBFC;">0x010000-0x10FFFF</span></p></td> <td><p><span style="background:#F9FBFC;">11110xxx 10xxxxxx 10xxxxxx 10xxxxxx(21</span><span style="background:#F9FBFC;">位)</span></p></td> </tr> </tbody> </table> 比如:“汉”在Unicode中的编码为0x6C49,在表格中的第三行,使用模板为:1110xxxx 10xxxxxx10xxxxxx。将0x6C49转换成二进制:0110 1100 0100 1001,将模板中的x依次替换11100110 10110001 10001001,即E6 B1 89。 ** UTF-16** UTF-16也是可变长度编码,使用2个或者4个字节来存储字符,但是会浪费存储空间。 UTF-16编码中,字符对应的代码点(code point)可能使用的是2或4个字节,因此2个字节就是一个代码单元(code unit),一个代码点(codepoint)可由1个或者2个代码单元(code unit)组成。 <table> <tbody> <tr> <td><p><span style="color:rgb(51,51,51);">十进制</span></p></td> <td><p><span style="background:#F9FBFC;">Unicode</span><span style="background:#F9FBFC;">编码</span></p></td> <td><p><span style="background:#F9FBFC;">UTF-16</span><span style="background:#F9FBFC;">字节流</span></p></td> </tr> <tr> <td><p><span style="color:#333333;background:#FFFFFF;">0-</span><span style="background:#F9FBFC;">65535</span><span style="background:#F9FBFC;">位</span></p></td> <td><p><span style="background:#F9FBFC;">0x</span><span style="color:#333333;background:#FFFFFF;">000000-</span><span style="background:#F9FBFC;">0x</span><span style="color:#333333;background:#FFFFFF;">00FFFF</span></p></td> <td><p><span style="color:#333333;background:#FFFFFF;">xxxxxxxx xxxxxxxx(16</span><span style="color:#333333;background:#FFFFFF;">位)</span></p></td> </tr> <tr> <td><p><span style="background:#F9FBFC;">65536-1114111</span><span style="background:#F9FBFC;">位</span></p></td> <td><p><span style="background:#F9FBFC;">0x</span><span style="color:rgb(51,51,51);">010000-</span><span style="background:#F9FBFC;">0x</span><span style="color:rgb(51,51,51);">10FFFF</span></p></td> <td><p><span style="color:rgb(51,51,51);">110110yy yyyyyyyy 110111xx xxxxxxxx</span><span style="background:#F9FBFC;">(20</span><span style="background:#F9FBFC;">位)</span></p></td> </tr> </tbody> </table> 平面0有一个专用区:0xE000-0xF8FF,有6400个码位。平面0的0xD800-0xDFFF,共2048个码位,是一个被称作代理区(Surrogate)的特殊区域。代理区的目的用两个UTF-16字符表示BMP以外的字符。所有大于0x00FFFF的码需要使用代理区的码点。 为了将一个16位无符号整数的UTF-16编码与二个16位无符号整数的UTF-16编码区分开来,Unicode编码的设计者将0xD800-0xDFFF保留下来,并称为代理区(Surrogate): <table> <tbody> <tr> <td><p><span style="color:rgb(51,51,51);">十进制</span></p></td> <td><p><span style="background:#F9FBFC;">Unicode</span><span style="background:#F9FBFC;">编码</span></p></td> <td><p><span style="color:rgb(51,51,51);">说明</span></p></td> <td><p><span style="color:rgb(51,51,51);">说明</span></p></td> </tr> <tr> <td><p><span style="color:#333333;background:#FFFFFF;">55296-56191</span></p></td> <td><p><span style="color:#333333;background:#FFFFFF;">0xD800</span><span style="color:#333333;background:#FFFFFF;">-0xDB7F</span>(896个)</p></td> <td><p><span style="color:#333333;background:#FFFFFF;">High Surrogates</span></p></td> <td><p><span style="color:#333333;background:#FFFFFF;">高位替代</span></p></td> </tr> <tr> <td><p><span style="color:rgb(51,51,51);">56192-56913</span></p></td> <td><p><span style="color:rgb(51,51,51);">0xDB80</span><span style="color:rgb(51,51,51);">-0xDBFF</span>(128个)</p></td> <td><p><span style="color:rgb(51,51,51);">High Private Use Surrogates</span></p></td> <td><p><span style="color:rgb(51,51,51);">高位专用替代</span></p></td> </tr> <tr> <td><p><span style="color:#333333;background:#FFFFFF;">56320-57343</span></p></td> <td><p><span style="color:#333333;background:#FFFFFF;">0xDC00</span><span style="color:#333333;background:#FFFFFF;">-0xDFFF</span>(1024个)</p></td> <td><p><span style="color:#333333;background:#FFFFFF;">Low Surrogates</span></p></td> <td><p><span style="color:#333333;background:#FFFFFF;">低位替代</span></p></td> </tr> </tbody> </table> 增补的16个平面(第2平面-第17平面),范围为:0x010000-0x10FFFF,需要使用2个代理码元表示,第一个码元为0xD800-0xDBFF(高位)范围是:11011000 00000000到11011011 11111111,第二个码元为0xDC00-0xDFFF(低位)范围是:11011100 00000000到11011111 11111111。 目前Unicode最大的码位是0x10FFFF,减去0x010000后最大值为0xFFFFF,完全可以用20位来表示。 把Unicode编码记作U,如果U≥0x10000,我们先计算U'=U-0x10000,然后将U'写成二进制形式:yyyy yyyy yyxx xxxx xxxx,U的UTF-16编码(二进制)就是:110110yyyyyyyyyy110111xxxxxxxxxx 比如:Unicode编码0x20C30,减去0x10000后,得到0x10C30,写成二进制是:0001 0000 1100 0011 0000。用前10位依次替代模板中的y,用后10位依次替代模板中的x,就得到:1101100001000011 1101110000110000,即0xD8430xDC30。 **UTF-32** UTF-32编码以32位无符号整数为单位。Unicode的UTF-32编码就是其对应的32位无符号整数。 UTF-32编码,一个代码点对应4个字节,因此4个字节就是一个代码单元(codeunit)。 <table> <tbody> <tr> <td><p><span style="color:rgb(51,51,51);">十进制</span></p></td> <td><p><span style="background:#F9FBFC;">Unicode</span><span style="background:#F9FBFC;">编码</span></p></td> <td><p><span style="background:#F9FBFC;">UTF-32</span><span style="background:#F9FBFC;">字节流</span></p></td> </tr> <tr> <td><p><span style="color:#333333;background:#FFFFFF;">0-</span><span style="background:#F9FBFC;">111411</span><span style="background:#F9FBFC;">位</span></p></td> <td><p><span style="background:#F9FBFC;">0x</span><span style="color:#333333;background:#FFFFFF;">000000-</span><span style="background:#F9FBFC;">0x</span><span style="color:#333333;background:#FFFFFF;">10FFFF</span></p></td> <td><p><span style="color:#333333;background:#FFFFFF;">xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx (32</span><span style="color:#333333;background:#FFFFFF;">位)</span></p></td> </tr> </tbody> </table> 比如:编码0x6C49使用UTF-32编码其结果为:0x00006C49。 **字节序** 字节序有两种,分别是大端(Big Endian, BE)和小段(Little Endian, LE)。根据字节序的不同,UTF-16实现为UTF-16BE和UTF-16LE,UTF-32实现为UTF-32BE和UTF-32LE。 根据字节序的不同,UTF-16可被实现为UTF-16LE或UTF-16BE,UTF-32可被实现为UTF-32LE或UTF-32BE。 <table> <tbody> <tr> <td><p><strong><span style="color:rgb(51,51,51);">Unicode</span><span style="color:rgb(51,51,51);">编码</span></strong></p></td> <td><p><strong><span style="color:rgb(51,51,51);">UTF-16LE</span></strong></p></td> <td><p><strong><span style="color:rgb(51,51,51);">UTF-16BE</span></strong></p></td> <td><p><strong><span style="color:rgb(51,51,51);">UTF32-LE</span></strong></p></td> <td><p><strong><span style="color:rgb(51,51,51);">UTF32-BE</span></strong></p></td> </tr> <tr> <td><p><span style="background:#F9FBFC;">0x006C49</span></p></td> <td><p><span style="color:#333333;background:#FFFFFF;">49 6C</span></p></td> <td><p><span style="color:#333333;background:#FFFFFF;">6C 49</span></p></td> <td><p><span style="color:#333333;background:#FFFFFF;">49 6C 00 00</span></p></td> <td><p><span style="color:#333333;background:#FFFFFF;">00 00 6C 49</span></p></td> </tr> <tr> <td><p><span style="color:rgb(51,51,51);">0x020C30</span></p></td> <td><p><span style="color:rgb(51,51,51);">43 D8 30 DC</span></p></td> <td><p><span style="color:rgb(51,51,51);">D8 43 DC 30</span></p></td> <td><p><span style="color:rgb(51,51,51);">30 0C 02 00</span></p></td> <td><p><span style="color:rgb(51,51,51);">00 02 0C 30</span></p></td> </tr> </tbody> </table> Unicode标准建议用BOM(Byte Order Mark)来区分字节序,即在传输字节流前,先传输被作为BOM的字符“零宽无中断空格”。这个字符的编码是FEFF,而反过来的FFFE(UTF-16)和FFFE0000(UTF-32)在Unicode中都是未定义的码位,不应该出现在实际传输中。 UTF编码的BOM: <table> <tbody> <tr> <td><p><strong><span style="color:rgb(51,51,51);">UTF</span><span style="color:rgb(51,51,51);">编码</span></strong></p></td> <td><p><strong><span style="color:rgb(51,51,51);">Byte Order Mark (BOM)</span></strong></p></td> </tr> <tr> <td><p><span style="color:#000000;background:#FFFFFF;">UTF-8 without BOM</span></p></td> <td><p><span style="color:#333333;background:#FFFFFF;"> 无</span></p></td> </tr> <tr> <td><p><span style="color:rgb(51,51,51);">UTF-8 with BOM</span></p></td> <td><p><span style="color:rgb(51,51,51);">EF BB BF</span></p></td> </tr> <tr> <td><p><span style="color:#333333;background:#FFFFFF;">UTF-16LE</span></p></td> <td><p><span style="color:#333333;background:#FFFFFF;">FF FE</span></p></td> </tr> <tr> <td><p><span style="color:rgb(51,51,51);">UTF-16BE</span></p></td> <td><p><span style="color:rgb(51,51,51);">FE FF</span></p></td> </tr> <tr> <td><p><span style="color:#333333;background:#FFFFFF;">UTF-32LE</span></p></td> <td><p><span style="color:#333333;background:#FFFFFF;">FF FE 00 00</span></p></td> </tr> <tr> <td><p><span style="color:rgb(51,51,51);">UTF-32BE</span></p></td> <td><p><span style="color:rgb(51,51,51);">00 00 FE FF</span></p></td> </tr> </tbody> </table> [GB2312]: https://blog.csdn.net/wdeng2011/article/details/80155548
还没有评论,来说两句吧...