Unicode学习

参考链接

  1. 终于搞懂了 Unicode、UTF-8、UTF-16
  2. ASCII
  3. Unicode

背景

ASCII编码

ASCII ((American Standard Code for Information Interchange): 美国信息交换标准代码)是基于拉丁字母的一套电脑编码系统,主要用于显示现代英语和其他西欧语言。它是最通用的信息交换标准,并等同于国际标准ISO/IEC 646。ASCII第一次以规范标准的类型发表是在1967年,最后一次更新则是在1986年,到目前为止共定义了128个字符 [1] 。

随着计算机的普及,不同国家和地区有出现了很多字符编码,如大陆的GB2312、港台的BIG5,日本的Shift JIS等。不同的字符编码往往会导致不同国家与地区之间交流变得十分困难,经常出现乱码的情况。

于是,为了统一所有国家和地区的字符编码,Unicode应运而生。

Unicode介绍

Unicode一般称为统一码,也叫万国码、单一码是计算机科学领域里的一项业界标准,包括字符集、编码方案等。Unicode 是为了解决传统的字符编码方案的局限而产生的,它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求。1990年开始研发,1994年正式发布1.0版本,2020年发布13.0版本。

Unicode 字符集的编码范围是 0x0000 - 0x10FFFF , 可以容纳一百多万个字符, 每个字符都有一个独一无二的编码,也即每个字符都有一个二进制数值和它对应,这里的二进制数值也叫码点 , 比如:汉字 “中” 的 码点是 0x4E2D, 大写字母 A 的码点是 0x41, 具体字符对应的 Unicode 编码可以查询 Unicode字符编码表。如下示例:

字符集和字符编码

字符集是很多个字符的集合,例如 GB2312 是简体中文的字符集,它收录了六千多个常用的简体汉字及一些符号,数字,拼音等字符。

字符编码是 字符集的一种实现方式,把字符集中的字符映射为特定的字节或字节序列,它是一种规则。

比如:Unicode 只是字符集,UTF-8、UTF-16、UTF-32 才是真正的字符编码规则

Unicode字符存储

Unicode 是一个符号集, 它只规定了每个符号的二进制值,但是符号具体如何存储它并没有规定

Unicode 字符集的编码范围是 0x0000 - 0x10FFFF,因此需要 1 到 3 个字节来表示(?此处存疑,还有极少量字符需要用4个字节来表示,如"𪚥")。

如果所有字符都用三个字节表示,那么对于那些一个字节就能表示的字符来说,有两个字节是无意义的,对于存储来说,这是极大的浪费。假如 , 一个普通的文本,大部分字符都只需一个字节就能表示,现在如果需要三个字节才能表示,文本的大小会大出三倍左右。

因此,Unicode 出现了多种存储方式,常见的有 UTF-8、UTF-16、UTF-32,它们分别用不同的二进制格式来表示 Unicode 字符。

UTF-8、UTF-16、UTF-32 中的 “UTF” 是 “Unicode Transformation Format” 的缩写,意思是"Unicode 转换格式",后面的数字表明至少使用多少个比特位来存储字符, 比如:UTF-8 最少需要8个比特位也就是一个字节来存储,对应的, UTF-16 和 UTF-32 分别需要最少 2 个字节 和 4 个字节来存储。

UTF-8编码

UTF-8是一种变长字符编码,被定义为将码点编码为 1 至 4 个字节,具体取决于码点数值中有效二进制位的数量。

UTF-8编码规则:

  1. 对于单字节的符号,字节的第一位设为 0,后面 7 位为这个符号的 Unicode 码。因此对于英语字母,UTF-8 编码和 ASCII 码是相同的、所以 UTF-8 能兼容 ASCII 编码,这也是互联网普遍采用 UTF-8 的原因之一;
  2. 对于 n 字节的符号( n > 1),最高位的字节的前 n 位都设为 1,第 n + 1 位设为 0,其他字节的前两位一律设为 10 。剩下的没有提及的二进制位,全部为这个符号的 Unicode 码。

如下表更加直观:

Unicode编码范围(16进制)UTF-8编码方式(二进制)
000000 - 00007F0xxxxxxx ASCII码
000080 - 0007FF110xxxxx 10xxxxxx
000800 - 00FFFF1110xxxx 10xxxxxx 10xxxxxx
01 0000 - 10 FFFF11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

根据上面表格,要解析 UTF-8 编码就很简单了。如果一个字节第一位是 0 ,则这个字节就是一个单独的字符,如果第一位是1 ,则连续有多少个1 ,就表示当前字符占用多少个字节。

如下图,以“中”字举例UTF-8的编码。

  1. 从字符集表可以查出“中”字的Unicode的编码为0x4E2D。使用二进制表示,共有16位。如上图步骤1;
  2. 通过上表格可以存储16位二进制数据,需要3个字节,即格式为1110xxxx 10xxxxxx 10xxxxxx。如上图步骤2;
  3. 将“中”字对应的二进制数据依次填入对应的UTF-8编码的数据位中,如步骤3;
  4. 最终可以得出“中”字的UTF-8编码是11100100 10111000 10101101,转换为十六进制为0xE4B8AD。

UTF-16编码

UTF-16也是一种变长字符编码,它将字符编码成2字节或者4字节。

UTF-16编码规则:

  1. 对于 Unicode 码小于 0x10000 的字符, 使用 2 个字节存储,并且是直接存储 Unicode 码,不用进行编码转换;
  2. 对于 Unicode 码在 0x10000 和 0x10FFFF 之间的字符,使用 4 个字节存储,这 4 个字节分成前后两部分,每个部分各两个字节,其中,前面两个字节的前 6 位二进制固定为 110110,后面两个字节的前 6 位二进制固定为 110111, 前后部分各剩余 10 位二进制表示符号的 Unicode 码 减去 0x10000 的结果;
  3. 大于 0x10FFFF 的 Unicode 码无法用 UTF-16 编码。

下表是Unicode编码对应UTF-16编码格式:

Unicode编码范围(16进制)具体Unicode码(二进制)UTF-16编码方式(二进制)字节
0000 0000 - 0000 FFFFxxxxxxxx xxxxxxxxxxxxxxxx xxxxxxxx2
0001 0000 - 0010 FFFFyy yyyyyyyy xx xxxxxxxx110110yy yyyyyyyy 110111xx xxxxxxxx4

“中” 字的 Unicode 码是0x4E2D,它小于0x10000。根据表格可知,它的 UTF-16 编码占两个字节,并且和 Unicode 码相同,所以 “中” 字的 UTF-16 编码为0x4E2D。

示例:

以这个字母Unicode码为0x10A6F 为例来说明 UTF-16 4 字节的编码,具体步骤如下:

UTF-32编码

UTF-32 是固定长度的编码,始终占用 4 个字节,足以容纳所有的 Unicode 字符,所以直接存储 Unicode 码即可,不需要任何编码转换。虽然浪费了空间,但提高了效率。

UTF字节序(大小端)

最小编码单元是多字节才会有字节序的问题存在,UTF-8 最小编码单元是一字节,所以 它是没有字节序的问题,UTF-16 最小编码单元是 2 个字节,在解析一个 UTF-16 字符之前,需要知道每个编码单元的字节序。

比如:前面提到过,“中” 字的 Unicode 码是 4E2D, “ⵎ” 字符的 Unicode 码是 2D4E, 当我们收到一个 UTF-16 字节流 4E2D 时,计算机如何识别它表示的是字符 “中” 还是 字符 “ⵎ” 呢 ?

所以,对于多字节的编码单元,需要有一个标记显式的告诉计算机,按照什么样的顺序解析字符,也就是字节序,字节序分为 大端字节序 和 小端字节序。

小端字节序简写为 LE( Little-Endian ),表示低位字节在前,高位字节在后, 高位字节保存在内存的高地址端,而低位字节保存在内存的低地址端。

大端字节序简写为 BE( Big-Endian ),表示高位字节在前,低位字节在后,高位字节保存在内存的低地址端,低位字节保存在在内存的高地址端。

注意:在某些工具网页做数据转换测试时失败的话,有可能是大小端问题导致的。

BOM

BOM 是"byte-order mark"的缩写,是"字节序标记"的意思, 它常被用来当做标识文件是以 UTF-8、UTF-16 或 UTF-32 编码的标记。

在 Unicode 编码中有一个叫做 “零宽度非换行空格” 的字符 ( ZERO WIDTH NO-BREAK SPACE ),用字符 FEFF 来表示。

对于 UTF-16 ,如果接收到以 FEFF 开头的字节流, 就表明是大端字节序,如果接收到 FFFE, 就表明字节流 是小端字节序。

UTF-8 没有字节序问题,上述字符只是用来标识它是 UTF-8 文件,而不是用来说明字节顺序的。“零宽度非换行空格” 字符的UTF-8 编码是 EF BB BF, 所以如果接收到以 EF BB BF 开头的字节流,就知道这是UTF-8 文件。

下面的表格列出了不同 UTF 格式的固定文件头:

UTF编码固定文件头
UTF-8EF BB BF
UTF-16LEFF FE
UTF-16BEFE FF
UTF-32LEFF FE 00 00
UTF-32BE00 00 FE FF

根据上面的 固定文件头,下面列出了 “中” 字在文件中的存储 ( 包含文件头 ):

编码固定文件头
Unicode 编码0X004E2D
UTF-8EF BB BF 4E 2D
UTF-16BEFE FF 4E 2D
UTF-16LEFF FE 2D 4E
UTF-32BE00 00 FE FF 00 00 4E 2D
UTF-32LEFF FE 00 00 2D 4E 00 00

Qt中转十六进制数据转UTF-8(16)方法

QByteArray hexByteArray = QByteArray::fromHex(text.toLatin1());
QString cnString = QString::fromUtf8(hexByteArray);

其中,text为UTF-8编码格式的十六进制数据,如QString text = "e4bda0";e4bda0为“你”字的UTF-8的十六进制数据。

如果转UTF-16,则将上面的QString::fromUtf8函数改为QString::fromUtf16即可。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值