前言:
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 个字节来存储
Unicode
世界上存在多种各样的编码方式,不同国家地区都有过自己地区的编码格式,这会导致同一个二进制数字,在不同的地区,被解释成不同的符号。
这时候Unicode代码就为此而生,他将世界上所有的符号都收纳其中,并且给每一个符号一个对应的唯一二进制代码(编码)。目前Unicode码表的规模已经达到了100多万个符号。
同时Unicode的前128个字符编码和ASSCII是一样的,这就说明Unicode兼容ASCII,即使用ASCII编码的程序可以直接用Unicode
Unicode的衍生问题
Unicode表能想象成为一本书,书中记录着每个字符对应的二进制代码编号,但是他没规定这个二进制的编号该如何存储。
简单理解的意思就是:计算机在读取字节的时候,他需要一套规范化方式,来做到--->读取多少个字节为一个字符---->开始转译--->转译成目标字符。
这时候就产生了UTF-32、UTF-16、UTF-8这三种编码存储方式
UTF编码
Unicode字符编码格式(Unicode Transformation Formats),简写为:UTF,即:将一个Unicode字符保存为字节序列的格式规范,用于文件存储、数据传输等。
UTF-8编码
首先要了解到UTF-8的编码格式,他是一种可变长度的编码,他的编码规则如下:
UTF-8编码方式(二进制) | |
---|---|
一字节 | 0xxxxxxx |
两字节 | 110xxxxx 10xxxxxx |
三字节 | 1110xxxx 10xxxxxx 10xxxxxx |
四字节 | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx |
案例:
“莞”的Unicode对应编码是839E(十六进制)
换算成二进制:1000 0011 1001 1110
绝大多情况下汉字在UTF-8中是三字节,而“莞”对应的二进制占16位,和三字节的能填入位数吻合(15位那些补0即可)
测试结果:
UTF-16编码
对于UTF-16编码是一种怎么样的编码,我查了很多资料,但是发现大家说的各异:有说他是一种定长两字节的编码方式、也有说他是一种可变不定长的编码方式(因为可以由两个字节或者四个字节来表示字符) 对此整理到以下笔记:
> UTF-16(Unicode Transformation Format-16)是一种定长的字符编码方式。在UTF-16中,每个字符通常由16位(2个字节)表示,但有一些特殊字符需要由32位(4个字节)表示。
> UTF-16使用了一种称为"码元(code unit)"的单位来表示字符。每个码元都由16位表示,并且可以表示一个基本多文种平面(Basic Multilingual Plane,BMP)中的字符。如果遇到位于辅助平面(Supplementary Planes)的字符,需要使用两个码元来表示。
> 因为UTF-16采用定长编码,所以每个字符在内存中占用的空间是固定的。需要注意的是,虽然UTF-16是定长编码,但它并不意味着每个字符都占用相同的存储空间。由于一些字符需要使用两个码元表示,因此一些字符可能会占用4个字节的空间。
> Unicode字符集被划分为两个主要部分:
> 基本多文种平面(Basic Multilingual Plane,BMP)和辅助平面(Supplementary Planes)。
> 基本多文种平面(BMP)涵盖了Unicode代码点范围从U+0000到U+FFFF的字符。这包括了大部分常用的字符,如拉丁字母、汉字、数字、标点符号等。基本多文种平面的字符可以使用一个码元(16位,2个字节)来表示。
> 辅助平面(Supplementary Planes)是位于基本多文种平面之外的字符范围。它包括了Unicode代码点范围从U+10000到U+10FFFF的字符。辅助平面中的字符无法通过单个码元来表示,需要使用两个码元(32位,4个字节)的代理对(surrogate pair)来表示。
> 辅助平面的划分边界是在U+FFFF和U+10000之间。U+FFFF是基本多文种平面的最后一个代码点,而U+10000是辅助平面的第一个代码点。
> 通过将辅助平面划分出来,Unicode可以容纳更广泛的字符集,包括一些特殊的符号、表情符号、历史文字等。
在这一大串文字可能很难理解,用我自己理解的话语就是:
在UTF-16中,由于两字节装不下Unicode收录的所有字符
所以用两字节的最小值到两字节的最大值: 0000~FFFF(十六进制)来划分为基本多文种平面
十六进制:0000->二进制:0000 0000 0000 0000
十六进制:FFFF->二进制: 1111 1111 1111 1111
而以FFFF高一位的10000为边界规定:10000~10FFFFF为辅助平面
十六进制:10000->二进制:0001 0000 0000 0000 0000
十六进制:10FFFF->二进制:0001 0000 1111 1111 1111 1111 //注意到辅助平面的最大边界并不是填满4字节
此时UTF-16的编译过程就变成了先判断字符Unicode表与10000(十六进制)的关系,也就是和码点65536(十进制)的关系。对于小于65536的字符,他的编译过程是直接转换成2个字节长的二进制数即可,而对于大于65536的码点他的编译过程比较麻烦。以下是UTF-16的一个编译流程:
如果码点<65536
如果码点>65536
** 码点>65536时候的流程图**
部分误区知识点:
UTF-16编码的资料补充
UTF-16并非是由于16个字节不够用才引入基本多文种平面(BMP)和辅助平面的划分的。实际上,UTF-16的设计初衷是为了向后兼容早期的Unicode实现。
早期的Unicode版本采用的是16位编码方案,其中每个字符都用16位(2个字节)表示。这被称为UCS-2(Universal Character Set-2),它可以表示65,536个字符,也就是Unicode代码点范围从U+0000到U+FFFF的字符。
然而,随着Unicode字符集的不断扩展,发现16位的编码空间不足以容纳所有字符。为了能够表示更多的字符,Unicode引入了辅助平面(Supplementary Planes),其中包含了位于U+10000到U+10FFFF范围的字符。
为了向后兼容UCS-2编码,UTF-16编码方案被设计出来。UTF-16使用16位码元来表示基本多文种平面(BMP)中的字符,而对于位于辅助平面的字符,使用代理对(surrogate pair)的方式,即两个16位码元(共四个字节)表示一个字符。
因此,UTF-16的设计是为了兼容早期的Unicode实现,并且能够适应Unicode字符集的扩展。它提供了一种灵活的编码方案,可以同时表示基本多文种平面和辅助平面的字符。
UTF-32编码
UTF-32是一种定长的编码方式,他的存储方式是:每4个字节表示一个字符。(被淘汰)
UTF-32缺点:
(1)存储简单字符时候浪费空间
案例:
存储字母a
其Unicode表中为:01100001
但是在UTF-32中为:00000000 00000000 00000000 01100001
(2)导致通信效率变低
UTF-32优点:
不用脑子,很暴力
UTF-8和UTF-16优缺点的比较
UTF16的存储格式是任意字符对应的数字都是两个字节或者四个字节来保存。
对于英文来讲,明明可以用一个字节能表示出来的,偏偏要用两个字节来表示,这造成了不必要的空间浪费。
但是对于非英文文字的来说,使用UTF-8会有性能浪费(因为要根据UTF-8编码规则解码,要在每个字节前做判断)
于是UTF-8和UTF-16的优劣很容易就看出来了.
如果全部英文或英文与其他文字混合,但英文占绝大部分,用UTF-8就比UTF-16节省了很多空间.
而如果全部是中文这样类似的字符或者混合字符中中文占绝大多数.UTF-16就占优势了,可以节省很多空间.[3]
UTF-16优缺点整理:
缺点:
1.UTF-16能表示的字符只有6万多
这是网络上大家认为的缺点,但是GPT中我了解到UTF-16能容纳的数量,远超6万个字符
GPT回答:
UTF-16能够表示的字符数量取决于Unicode字符集中的范围。Unicode定义了一共1,114,112个可能的代码点。
在UTF-16中,基本多文种平面(BMP)的字符使用单个码元(16位,2个字节)表示,可以表示65,536个代码点(U+0000到U+FFFF)。
辅助平面的字符需要使用代理对(surrogate pair)表示,每个代理对由两个码元(32位,4个字节)组成。辅助平面有1,048,576个代码点(U+10000到U+10FFFF),因此UTF-16可以表示这个范围内的字符。
所以,UTF-16总共能够表示基本多文种平面(BMP)和辅助平面中的1,114,112个Unicode代码点。需要注意的是,并非所有的Unicode代码点都有对应的字符或者被分配了字符。实际上,Unicode字符集中只有一部分被分配给了具体的字符,而剩余的空间被保留用于未来的扩展。
个人理解是UTF-16中,用两个字节是最优的存储方式,但只能收纳到6万多个字,再往后必须要使用辅助平面字符,但是辅助平面字符的编译过程需要的性能是个大缺点。
2.UTF-16 存在大小端字节序问题
这个问题在进行信息交换时特别突出——如果字节序未协商好,将导致乱码;如果协商好,但是双方一个采用大端一个采用小端,则必然有一方要进行大小端转换,性能损失不可避免(大小端问题其实不像看起来那么简单,有时会涉及硬件、操作系统、上层软件多个层次,可能会进行多次转换)> > > >
这个同样是网上所描述到的缺点,但是我没能理解原因。
3.容错率低
当其中每个字节或者每个比特位丢失时候,会导致这个数据全部错乱,没办法纠错
4.部分文字空间利用率低
对于英文来讲,明明可以用一个字节能表示出来的,偏偏要用两个字节来表示,这造成了不必要的空间浪费。
优点:
1.部分文字性能效率与空间存储率高:
如果某种文字他的Unicode表处于0000 0001 0000 0000 到 1111 1111 1111 1111之间那么他就很合适UTF-16
UTF-8的优缺点整理:
缺点:
1.使用地区不平衡
文字出现频率在Unicode中,常在三个字节以上的国家而言空间冗余程度增加了。
而对于英语为母语的国家来说是非常方便的,因为他兼容了ASCII编码,存储空间上完全不浪费
2.变长编码的性能问题
优点:
1.字符空间足够大
2.不存在大小端字节序问题,信息交换时非常便捷
3.容错性高,局部的字节错误(丢失、增加、改变)不会导致连锁性的错误,因为 UTF-8 的字符边界很容易检测出来
资料来源:
标签:文字,编码,UTF,字节,16,Unicode,字符 From: https://www.cnblogs.com/ethereal258/p/17556236.html[1]: Unicode 和 UTF-8 之间的关系转载\ - Tsingke - 博客园 (cnblogs.com)(https://www.cnblogs.com/tsingke/p/10853936.html)
[2]: 一听就懂字符集、ASCII、GBK、UTF-8、Unicode、乱码、字符编码、解码问题的讲解_哔哩哔哩_bilibili(https://www.bilibili.com/video/BV1xD4y1y7yc/?spm_id_from=333.337.search-card.all.click&vd_source
[3]: Unicode、UTF-8、UTF-16之间的区别 - 知乎 (zhihu.com)(https://zhuanlan.zhihu.com/p/259386795)