Unicode学习笔记

来源:互联网 发布:mac输入人民币符号 编辑:程序博客网 时间:2024/06/05 16:11

前言:最近在学习一个关于Emoji的开源项目,学习过程中了解到Emoji是Unicode字符集中的一部分。平时工作、学习中经常会碰到Unicode、UTF-8等等这些字符编码相关的名词,往往不容易搞清楚之间的关系。于是便用了一些时间学习了Unicode的相关知识。本篇文章作为Unicode的学习笔记。


在了解Emoji的过程中,发现了一个网站Emoji Unicode Tables,该网站提供了Emoji表情与Unicode字符集的对应关系。

如下图所示(图一):

这里写图片描述

点击表中的Unicode一栏,页面跳转到如下页面(图二):

这里写图片描述
该页面详细显示了该Emoji对应的Unicode值以及UTF-8、UTF-16 LE、Sorrogates编码,但是它们之间是如何转换的,以及它们之间有着怎样的关系。

一、Unicode

Unicode(统一码、万国码、单一码)是计算机科学领域里一项业界标准。它对世界上的大部分的文字系统进行了整理、编码,使得电脑可以用更为简单的方式来呈现和处理文字。Unicode为每一个符号指定一个独一无二的编码。

Unicode目前的编码范围为0x0000~0x10FFFF,并将该范围分为17个平面,每个平面拥有65536(2^16)个码位,共1114112个,但是目前只用了少数的几个平面。其中最重要的一个平面为平面0(Basic Multilingual Plane,简称BMP),该平面的编码范围为0x0000~0xFFFF,包含了世界上常用的语言文字和符号,是Unicode最基础和常用的部分。

  • BMP中的字符如果用UTF-8编码,需要1-3个字节,UTF-16编码表示需要2个字节
  • BMP以外的字符的字符以UTF-8编码需要4个字节,UTF-16编码需要4个字节
  • 另外还有一种UTF-32编码,该编码使用较少,UTF-32对任意Unicode字符编码都需要4个字节
  • BMP中0xD800~0xDFFF范围内共2048个码位没有对应任何字符,该范围称为Surrogate。该范围又分为两段,0xD800~0xDBFF包含的1024个码位称为high surroagte,0xDC00~0xDFFF包含的1024个码位成为low surroagte,可以通过这两个范围组成四个字节的surroagte pair来一一对应到BMP之外的字符。

那么Unicode和UTF-8、UTF-16是什么关系呢?简单的说,Unicode是字符集而UTF-8这些是Unicode的不同编码存储方式。

二、UTF-8

UTF-8使用1~4个字节表示一个符号,不同的符号所占的字节数不同,所以UTF-8也称为可变长编码。
UFT-8的编码方式很简单,只有两条:
1. 对于单个字节,字节的第一位为0,剩下的7位为该符号的unicode码。对于英文字母来说,其UTF-8编码和ASCII编码相同。
2. 对于长度为n(n >1)的字节,第一个字节的前n位都为1,第n+1位为0。接下来的字节,每个字节的前两位均为10。剩下的位数使用该符号unicode码的二进制填充。

Unicode编码范围(16进制) UTF-8编码(2进制) 0x0000 0000~0x0000 007F 0xxxxxxx 0x0000 0080~0x0000 07FF 110xxxxx 10xxxxxx 0x0000 0800~0x0000 FFFF 1110xxxx 10xxxxxx 10xxxxxx 0x0001 0000~0x0010 FFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

根据上表编码规则很容易对UTF-8进行解读。如果第一位为0,则该单个字节就表示一个字符;如果第一位为1,则有多少连续的1就表示该字符占几个字节。

现在以“中文”的“中”为例,“中”的unicode码为4E2D(‭100111000101101‬)属于上表中的第三行范围,所以”中”的UTF-8编码应该占3个字节(1110xxxx 10xxxxxx 10xxxxxx),用“中”的unicode码的二进制位进行填充,不足的位用0补齐。所以“中”的UTF-8编码为‭E4 B8 AD‬(11100100 10111000 10101101)。

三、UTF-16

UTF-16为Unicode的另一种编码方式。位于BMP(范围0x0000~0xFFFF)中的符号,UTF-16编码可直接使用该字符的unicode码直接表示,这时UTF-16编码占2个字节。位于BMP外的符号,如果需要用UTF-16编码表示,需要将unicode码转换为四个字节的代理对(surroagte pair)来表示。

其转换过程如下:

这里写图片描述

如图,具体过程如下:

  • 需要转换使用代理对表示,说明该符号unicode的码值在0xFFFF<x<=0x10FFFF内
  • 用该码值减0x10000,得到的结果范围为0x0~0xFFFFF,可使用20bit进行表示。
  • 用该值的前10bit与0xD800相加,后10bit与0xDC00相加,最后得到的一个24bit的代理对,可以用四个字节表示。

如本篇文章的图二所示,该Emoji符号的Unicode值为U+1F601,范围在0xFFFF<x<=0x10FFFF内,所以可以转换为代理对进行表示,U+1F601减去0x10000的值为0xF601,二进制表示为‭00001111011000000001‬。用0xD800加前10bit,即0xD800 + 0b0000111101,结果为0b1101100000111101。后10bit与0xDC00相加,即0xDC00 + 0b1000000001,结果为0b1101111000000001。转化为四个的字节的16进制为‭D83D‬ ‭DE01‬,即图二中的Surrogates值。

四、Little endian和Big endian

Unicode码可以采用UCS-2格式直接存储。以汉字”严”为例,Unicode码是4E25,需要用两个字节存储,一个字节是4E,另一个字节是25。存储的时候,4E在前,25在后,就是Big endian方式;25在前,4E在后,就是Little endian方式。
这两个古怪的名称来自英国作家斯威夫特的《格列佛游记》。在该书中,小人国里爆发了内战,战争起因是人们争论,吃鸡蛋时究竟是从大头(Big-Endian)敲开还是从小头(Little-Endian)敲开。为了这件事情,前后爆发了六次战争,一个皇帝送了命,另一个皇帝丢了王位。
因此,第一个字节在前,就是”大头方式”(Big endian),第二个字节在前就是”小头方式”(Little endian)。
那么很自然的,就会出现一个问题:计算机怎么知道某一个文件到底采用哪一种方式编码?
Unicode规范中定义,每一个文件的最前面分别加入一个表示编码顺序的字符,这个字符的名字叫做”零宽度非换行空格”(ZERO WIDTH NO-BREAK SPACE),用FEFF表示。这正好是两个字节,而且FF比FE大1。
如果一个文本文件的头两个字节是FE FF,就表示该文件采用大头方式;如果头两个字节是FF FE,就表示该文件采用小头方式。

结语,到此为止图二表格中所有的值,已经都可以读懂了:)

参考:

  • 字符编码笔记:ASCII,Unicode和UTF-8
  • 图说我对Unicode的几点理解
  • Emoji的编码以及常见问题的解决方法
  • Unicode 是不是只有两个字节,为什么能表示超过 65536 个字符?
0 0