Unicode 字符集和编码
问大家一个问题,大家是否常常对 ASCII、Unicode 、UTF-8 、UTF-16 有着似懂非懂的感觉?
如果有,那大家就请小编来一起来探讨探讨吧
在了解这些之前,有两个概念,需要大家先明确一下:【字符集,字符编码】
什么是字符集,字符编码呢?
字符集
字符集是一个定义好的字符集合,其中每个字符都有唯一的数值代码(码点)【ps:文章中会多次出现这个名词,还请大家记下】。字符集是字符和对应码点之间的映射关系。字符集是一个抽象的概念,不涉及字符在内存或磁盘上的物理存储方式【ps:不是存储方式】。它们仅定义字符和码点之间的映射关系。字符集提供了了解可用于使用的字符的方式,但它们不定义这些字符如何被编码。简单来说,我们可以把字符集看成是一本字典,每一个字就是字符,假设一本字典上有 10W 个字,那我们就可以把每个字对应上 0-100000 上的数值。每个值代表着一个字。
举例:在 ASCII 字符集中表示字符 A 的对应值是 65。这里的映射关系是 A = 65。我们可以这样理解。
字符编码
通过字符集,我们知道了字符与数值之间的对应关系,我们是不是会直观的以为只要将对应的数值以二进制的方式存储到内存和硬盘就可以了呢?其实不然,那如何将对应的数值存储到计算机的存储介质中呢?这就涉及到对应的字符编码规则了。字符编码它涉及将字符(码点)转换为二进制代码,以便进行存储和通信。
不同的字符编码方案使用不同的规则和格式来将字符表示为二进制数据。例如,ASCII、UTF-8和UTF-16都是字符编码方案,每种方案都有自己的字符编码方法。
根据字符集,字符编码和我们进行总结和区分一下:
字符集:ASCII,Unicode,ISO 8859,GB2312 等等
字符编码:ASCII,UTF-8,UTF-16
ps:ASCII既是字符集,也是字符编码,因为它定义了一组字符和对应的码点,同时它定义了如何将码点映射为二进制代码。【其实不仅仅是 ASCII,ISO 8859 等很多都是, 都是即表示字符集,又表示字符编码】
难怪我们经常弄混呢,哈哈哈
到这里我们就来聊聊 Unicode 的吧
Unicode
经过上面的介绍,不知道大家有没有发现一个问题,就是那么多的字符集,难道不乱嘛?而且会不会出现,某个字符集定义的码点,另一个字符集也在用这个码点,就造成了相同的码点,不同的字符集对应的却是不同的字。那遇到这个问题怎么办???
这个就是我们 Unicode 出现的原因,哈哈哈
Unicode 的全称 Universal Multiple-Octet Coded Character Set 。
Unicode 也是一种字符集,它是计算机科学领域中用来表示世界上几乎所有的字符和符号的标准。Unicode为每个字符分配了一个唯一的码点(code point),用十六进制表示。
没错,Unicode 就是一种字符集,类似一本字典,里面记录了全世界,所有国家和地区使用的字符和符号。Unicode 并没有规定具体的编码规则方式,它只是为每个字符分配了唯一的码点。
Unicode 的码点
那 Unicode 的码点是怎么样的,怎么表示呢?
Unicode的码点是一个32位的整数,用十六进制表示,通常加上前缀"U+"。例如,字母 "A" 的 Unicode 码点为 U+0041,汉字 "中" 的Unicode码点为U+4E2D。
- 码点范围:Unicode的码点范围从U+0000到U+10FFFF,共有1,114,112个码点。其中,U+0000到U+FFFF之间的码点被称为基本多语言平面(BMP),它包含了绝大部分常用字符。从U+010000到U+10FFFF 之间的码点被称为16个扩展平面,用来存储较少使用的字符。
- 分配原则:Unicode的码点是根据字符的使用频率和重要性来分配的,常用字符和符号的码点在前面,不常用的字符在后面。Unicode还会不断进行补充和更新,以适应新的字符需求。
- 非字符码点:Unicode还定义了一些特殊的码点,用于表示非字符的用途,比如U+FFFE和U+FFFF被保留用于字节序标记(Byte Order Mark,BOM),U+FEFF被用作零宽度非断空格(Zero Width No-Break Space,BOM)等。
Unicode 的码点定义和规则确保了全球范围内几乎所有字符和符号都能得到唯一的标识,使得计算机能够正确地处理和显示不同语言的文本,并且为未来的字符扩展提供了足够的空间。
ps:Unicode 是兼容 ASCII,具体来说,Unicode的码点范围从U+0000到U+007F与ASCII完全一致,其中U+0000到U+007F的256个码点与ASCII字符集的128个字符是一一对应的。这意味着ASCII字符在Unicode中可以用相同的编码表示,例如字母"A"在ASCII中的编码是65,在Unicode中也是U+0041。但是对应其他的字符集并不一定完全兼容。
现在我们知道了一个在 U+0000到U+10FFFF 范围内的值,表示的就是一个 Unicode 字符。那实际存储到内存和硬盘的数据也是该范围内值的二进制表示形式嘛?
不一定呦,哈哈哈
实际存储在内存和硬盘中的数据是以二进制形式表示的,但并不是直接将Unicode字符的码点值存储到内存和硬盘中。相反,Unicode字符在计算机中的存储和传输一般采用特定的字符编码方式。
那么有那些字符编码方式呢?
UTF-8、UTF-16和UTF-32
常见的 Unicode 编码方式包括UTF-8、UTF-16 和 UTF-32。
都是 UTF- 开头,有意思啊,后面的 8 ,16 , 32 又表示什么意思呢?他们又有什么区别呢?
后面的数字其实表示的是存储一个 Unicode 字符,所需要的大小。UTF-8 最小需要一个字节,UTF-16 最小需要 2 个字节,UTF-32 最小需要 4 个字节。
UTF-8 的编码规则
UTF-8是一种可变长度的编码方式,使用1到4个字节来表示不同的Unicode字符。其中,U+0000到U+007F之间的字符(即ASCII字符集)使用一个字节表示,U+0080到U+07FF之间的字符使用两个字节表示,U+0800到U+FFFF之间的字符使用三个字节表示,U+10000到U+10FFFF之间的字符使用四个字节表示。
UTF-8编码规则的细节如下:
对于1个字节的UTF-8编码:
ASCII字符(U+0000至U+007F)使用1个字节表示,对应的二进制表示格式为0xxxxxxx,其中x是ASCII字符的7位二进制值。
对于2个字节的UTF-8编码:
格式为110xxxxx 10xxxxxx,其中x是字符的Unicode码点的高5位和低6位。
对于3个字节的UTF-8编码:
格式为1110xxxx 10xxxxxx 10xxxxxx,其中x是字符的Unicode码点的高4位、中间6位和低6位。
对于4个字节的UTF-8编码:
格式为11110xxx 10xxxxxx 10xxxxxx 10xxxxxx,其中x是字符的Unicode码点的高3位、中间6位、中间6位和低6位。
让我们来举例说明UTF-8编码的过程:
字符 "A" 的Unicode码点是U+0041。由于U+0041属于ASCII字符范围,它可以用1个字节表示,所以UTF-8编码为:01000001。
字符 "€" 的Unicode码点是U+20AC。它落在U+0080至U+07FF的范围内,需要用2个字节表示。首先,获取U+20AC的二进制值为:00100000 10101100。然后,将前5位添加到110,后6位添加到10,得到UTF-8编码为:11000010 10101100。
字符 "😀" 的Unicode码点是U+1F600。它落在U+0800至U+FFFF的范围内,需要用3个字节表示。首先,获取U+1F600的二进制值为:00011111 01100000 00000000。然后,将前4位添加到1110,中间6位添加到10,后6位添加到10,得到UTF-8编码为:11100000 10011111 10000000。
这样,UTF-8编码根据字符的Unicode码点的范围来确定所需的字节数,并使用特定的位模式进行编码,确保了所有Unicode字符都能被正确地表示。
UTF-16 的编码规则
UTF-16是一种定长或可变长度的编码方式,使用2或4个字节来表示不同的Unicode字符。其中,U+0000到U+FFFF之间的字符使用两个字节表示,U+10000到U+10FFFF之间的字符使用四个字节表示。
字符长度:
UTF-16中的字符长度固定为2个字节(16位)。每个字符在UTF-16中占据两个字节的存储空间。
编码方式:
UTF-16使用两个字节来编码所有的字符,其中基本平面(BMP,U+0000至U+FFFF)的字符使用单个16位编码(16 bits),而辅助平面(SMP,U+010000至U+10FFFF)的字符使用一对16位编码(32 bits)。
字节序:
由于UTF-16是固定长度的,它涉及字节顺序问题,即字节序(Byte Order)。UTF-16采用"Big Endian"或"Little Endian"的字节序方式存储数据。
【 对于 Big Endian 和 Little Endian 大端小端的存储问题 】
UTF-16编码规则的细节如下:
对于BMP(基本多文种平面)的字符(U+0000至U+FFFF):直接使用16位编码表示,该编码与字符的Unicode码点相同。
对于辅助平面(SMP)的字符(U+010000至U+10FFFF):采用一对16位编码(32 bits)表示。首先,需要进行一次编码转换,将字符的Unicode码点转换为UTF-16编码的格式。
让我们来举例说明UTF-16编码的过程:
字符 "A" 的Unicode码点是U+0041。由于U+0041属于BMP范围,它可以用单个16位编码表示,所以UTF-16编码为:00000000 01000001。
字符 "€" 的Unicode码点是U+20AC。它属于BMP范围,可以用单个16位编码表示,所以UTF-16编码为:00100000 10101100。
字符 "😀" 的Unicode码点是U+1F600。它属于辅助平面(SMP)范围,需要用一对16位编码(32 bits)表示。首先,需要将U+1F600转换为UTF-16编码格式。该过程如下:
U+1F600的二进制值为:00000001 11110110 00000000。
将U+1F600拆分成高位和低位:00000001 111101 和 10 00000000。
高位添加 U+D800(十进制:55296),低位添加 U+DC00(十进制:56320)。
得到UTF-16编码:11011000 00000001 11011000 10000000。
这样,UTF-16编码使用固定的2个字节(16位)表示字符,并且能够正确地处理所有Unicode字符,包括基本平面和辅助平面的字符。需要注意的是,UTF-16涉及字节序的问题,应用程序在处理UTF-16编码时需要正确处理字节序,以确保正确解析字符。
UTF-32 的编码规则
UTF-32是一种定长编码方式,使用4个字节来表示所有的Unicode字符,无论码点值是多少。
字符长度:
UTF-32中的字符长度固定为4个字节(32位)。每个字符在UTF-32中占据四个字节的存储空间。
编码方式:
UTF-32使用四个字节来编码所有的字符,每个字符的UTF-32编码直接与其Unicode码点相同。
无需编码转换:
由于UTF-32是固定长度的,不涉及字节序问题,因此在不同平台或系统之间的数据传输和存储时无需进行编码转换。
UTF-32编码规则的细节如下:
对于任何Unicode字符:直接使用32位编码表示,该编码与字符的Unicode码点相同。
让我们来举例说明UTF-32编码的过程:
字符 "A" 的Unicode码点是U+0041。由于UTF-32编码固定为32位,它可以用单个32位编码表示,所以UTF-32编码为:00000000 00000000 00000000 01000001。
字符 "€" 的Unicode码点是U+20AC。UTF-32编码固定为32位,所以UTF-32编码为:00000000 00000000 00100000 10101100。
字符 "😀" 的Unicode码点是U+1F600。UTF-32编码固定为32位,所以UTF-32编码为:00000000 00000001 11110110 00000000。
UTF-32编码使用固定的4个字节(32位)表示每个字符,因此能够简单明了地表示Unicode字符,且不需要进行编码转换。UTF-32在某些情况下可能会占用更多的存储空间,但在处理和处理Unicode字符时非常方便和高效。由于其固定长度的特性,UTF-32通常在内存中用于处理Unicode字符,而在数据传输和存储时,考虑到节省空间和字节序问题,UTF-8和UTF-16更常见。
这里解释一下比较普片的问题,为什么需要那么多字符编码方式,好像使用 UTF-32 就已经兼容和包含了 UTF-8 和 UTF-16。为什么还需要 UTF-8 和 UTF-16 呢?而且我们日常中使用 UTF-8 是最多的。
字符编码方式之所以有多种选择,是因为每种编码方式都有自己的优缺点,适用于不同的使用场景和需求。
- 存储空间效率:
- UTF-32:UTF-32是一种固定长度的编码方式,每个字符都需要4个字节来表示。尽管UTF-32可以确保所有Unicode字符都能被简单明了地表示,但它会占用较多的存储空间,尤其是对于仅包含ASCII字符的文本,使用UTF-32会浪费存储空间。
- UTF-8:UTF-8是一种可变长度的编码方式,ASCII字符(U+0000至U+007F)只需要1个字节表示,而其他Unicode字符需要2至4个字节表示。UTF-8在存储纯ASCII字符文本时非常高效,因为它只需要与ASCII编码相同的存储空间。对于多数的英文文本和大部分现代应用,UTF-8相对于UTF-32更节省存储空间。
- 兼容性:
- UTF-8:UTF-8是ASCII的超集,所有合法的ASCII文本都是合法的UTF-8文本。这意味着UTF-8可以无缝地与ASCII编码兼容。这种特性在网络传输和文件交换中非常有用,因为大部分网络协议和文件格式都是基于ASCII编码的。
- UTF-16:UTF-16作为固定长度的编码方式,不能像UTF-8那样直接兼容ASCII编码。对于只包含ASCII字符的文本,UTF-16需要使用两个字节来表示一个字符,造成了一定的浪费。
- 处理效率:
- UTF-8:UTF-8在处理文本时,由于可变长度的特性,特别适用于字符串的操作,例如搜索、截取等操作。此外,UTF-8也适合网络传输,因为它节省带宽。
- UTF-16:UTF-16在处理文本时需要考虑字节序问题(Big Endian或Little Endian),这可能增加处理的复杂性。对于字符操作,UTF-16通常需要更多的处理和存储资源。
总结:UTF-8、UTF-16和UTF-32都有各自的优势和适用场景。UTF-8相对于UTF-16和UTF-32更加灵活、节省存储空间,在处理纯ASCII文本和网络传输时表现优秀,因此在日常使用中最为普遍。UTF-16通常用于内存中的文本处理和操作,而UTF-32则保证了所有字符都能被简单明了地表示,适用于对所有字符均等对待的场景。根据不同的需求和使用场景,选择合适的字符编码方式是非常重要的。
至此不知道大家是否明白了能,哈哈哈,如果有疑问也欢迎给小编留言
更多内容欢迎关注 [ 小巫编程室 ] 公众号,喜欢文章的话,也希望能给小编点个赞或者转发,你们的喜欢与支持是小编最大的鼓励,小巫编程室感谢您的关注与支持。good good study day day up