UTF是什么的简写

UTF是 Unicode* Transformation Format 的简写。直译就是Unicode的转换格式。 (* U同时也是UCS的简写,UCS和Unicode是两个组织订立的标准,但是码表基本兼容,在此略过不提) UTF-8, UTF16, UTF-32分别对应8bit, 16bit, 32bit长度的转换格式。在下文详细描述。

什么是Unicode

对计算机而言,什么汉字偏旁部首,英文字母,日语假名那都是不存在的。大千世界的纷繁复杂到了计算机里最终都化成了0和1组成的二进制数。 那怎么让计算机看懂人类用的字符呢?很简单,把字符都定义成二进制数就行了,一个字符一个数,一个萝卜一个坑,简单易懂。

ASCII

计算机发展早期大多采用ASCII编码。其使用一个字节,即8位二进制数来表示字符。 ASCII的起始位为0,可以表示27即128个字符。这些字符包含了0-9的数字,各种常用符号,大小写英文字母以及一些控制符等。 如字符a对应的ASCII编码为0b01100001或者0x61 (* 0b代表二进制,0x代表十六进制,下同) 或者以我们常用的十进制为编号97

ASCII起源于电报时代,27即128个字符已足以满足当时需求。直到1970年代8位处理器开始普及,字节(Byte)的单位也由4位,6位等统一为8位二进制数(8bit)并沿用至今。ASCII在8位二进制数中首位置0。后来也发展出extended ASCII编码,对首位为1的编码定义,充分利用了一个字节256个编码。但即便如此256个编码也不可能满足全世界对字符编码的需求。

随着计算机技术的高速发展与普及,各地都发展出了各自的编码标准,以满足在计算机上显示,处理本国语言的需求。然而一时之间标准频出,彼此之间又多不兼容,导致乱码丛生,一发不可收拾。
把Big5标准下的曹操用GB标准打开就成了变巨,玩过光荣的曹操传和三国志(繁体版)的朋友应该很熟悉

Unicode

这时对一个全球通用的字符编码标准的需求越来越高,而Unicode就是为此而诞生。 Unicode的解释可以参照wikipedia。以下摘自wiki-Unicode词条

Unicode(中文:万国码、国际码、统一码、单一码)是计算机科学领域里的一项业界标准。它对世界上大部分的文字系统进行了整理、编码,使得电脑可以用更为简单的方式来呈现和处理文字。 Unicode 伴随着通用字符集的标准而发展,同时也以书本的形式[1]对外发表。Unicode 至今仍在不断增修,每个新版本都加入更多新的字符。当前最新的版本为2019年5月公布的12.1.0[2],已经收录超过13万个字符(第十万个字符在2005年获采纳)。Unicode涵盖的数据除了视觉上的字形、编码方法、标准的字符编码外,还包含了字符特性,如大小写字母。

简单说来Unicode就是一个包含世界上大多数国家的文字/符号的编码标准。它给每个字符一个编号,形成了一个庞大的字符集。

Unicode的表示

Unicode拓展自ASCII,因此上文的a在Unicode中的编码不变,为U+0061。 Unicode编码为U+加上至少4位十六进制数字的形式表示。例如这个汉字的Unicode为U+6211的Unicode为U+6c49。最新的Unicode12.1版本中除去tag,被注册的最大的编码为中日韩兼容表意文字,为U+2F9FF
https://unicode-table.com/cn/#2F9FF
U+2F9FF](https://raw.githubusercontent.com/itsuhi-shu/itsuhi-shu.github.io/master/_posts/assets/samplegu.webp)

Unicode Transformation Format (UTF)

如上文所述,一个字节8位,至多表示28=256个编码。而Unicode以收录十多万个字符,显然用一个字节无法表示所有的Unicode编码。 两个字节16位,至多表示216=65536个编码。显然也不足够。 再翻个倍四个字节32位,就能表示232=4294967296个编码。所以Unicode通常使用1-4个字节来表示字符。 计算机很蠢,看到一大堆数据便直接一整套全都读取进去。如何分割字节,如何把编码转化成字符,必须由人来告诉它。 UTF即是告诉计算机如何存储读取字符的一种格式。

UTF-32

上文提到,4个字节足以表示任何Unicode,因此UTF-32就采用每个字符4个字节的储存形式(4个字节为32位,因此是UTF-32)。 UTF-32格式下,每个字符会读取4个字节,然后按标准转化为字符。
UTF-32每次读取4个字节

  • 这种格式的好处在于,读取效率尤其高。计算机只管4个字节4个字节地读,不需要考虑其他。
  • 而缺点就在于,空间占用率过大。 对于英语国家而言,常用的字符已包含在ASCII中,也就是说一个字节就能满足大多数使用场景。而UTF-32的格式下,即便是ASCII中的字符,也要占用4个字节,是原来的4倍。 (4倍,那是什么概念!!四舍五入就是一个亿啊!!)

考虑到UTF-32造成的浪费,一般情况下UTF-8和UTF-16才是主流选择。

UTF-8

UTF-8是最常用的一种Unicode转换格式。也是最灵活的一种格式。 UTF-8解决了UTF-32占用空间过大的问题。8即是8位,代表着读取单位为8bit,即1个字节。 ASCII的字符只用这1个字节便可以转换,而其他字符则由多个字节组成。 由于采用了这种组合式的做法。UTF-8编码的每个字节需要用字节开头的几位(bit)来告诉计算机一些信息,比如这个字节是某个字符的首字节,由多少个字节组成,亦或者这个字节是别的字符的后续。 其具体规则如下,参考自https://stackoverflow.com/a/33349765

Binary Hex Comments
0xxxxxxx 0x00..0x7F Only byte of a 1-byte character encoding
单字节字符
10xxxxxx 0x80..0xBF Continuation byte: one of 1-3 bytes following the first
多字节字符的后续字节
110xxxxx 0xC0..0xDF First byte of a 2-byte character encoding
双字节字符的首字节
1110xxxx 0xE0..0xEF First byte of a 3-byte character encoding
三字节字符的首字节
11110xxx 0xF0..0xF7 First byte of a 4-byte character encoding
四字节字符的首字节
  • 单字节字符首位取0
  • n字节字符的首字节前n位取1,n+1位取0
  • 多字节字符的后续字节前两位取10

UTF-8的转换方法

UTF-8解决了常用字符(1-3字节)在UTF-32下占用空间过大的问题。但其缺点也同样存在,

  • 其一,相对于UTF-32,UTF-8的读取效率有所降低,毕竟需要读取标识位并加以转化。 相比直接读取4个字节,会消耗一小部分性能。
  • 其二,Unicode中汉字区间为0x4E00→0x9FA5,在UTF-8下都需要占用三个字节,如上图字,UTF-8下编码为E58F8B,但其Unicode编码其实是U+53CB,理论上只使用两个字节就能表示,也存在着空间浪费的情况。 美国人是开心了,中国人民不干了

UTF-16

UTF-16是一种折衷的方案,UTF-16使用2或者4个字节来表示字符。 U+0000-U+FFFF的字符均用2个字节表示,而此范围以外的字符用4个字节表示。 由于常用字符以及主要国家的文字字符基本都包含在这个范围中,所以在绝大多数情况下字符都只需2个字节,通用性很高。 世界人民皆大欢喜

UTF-16可以表示U+0000-U+10FFFF的编码区间,有一千多万个编码,在现阶段足够使用。 其编码规则如下

  1. U+0000 - U+FFFF 使用两个字节直接表示。
  2. U+10000 - U+10FFFF a. 将Unicode编码减去0x10000(因为0x10000以下的编码已包含在1中),得到的编码的区间是0x00000-0xFFFFF(最大为5位16进制数,即20位二进制数,20bit)。 b. 将得到的编码分为两部分,每一部分为10位二进制数(10bit)。 c. 在高位的10bit前补上6位的标识110110,即加上0b 1101 1000 0000 0000=0xD800,这个0xD800也称为前导代理(lead surrogates)。 d. 在低位的10bit前补上6位的标识110111,即加上0xDC00,这个0xDC00也称为后尾代理(trail surrogates)。

可参照wiki的示例
UTF-16 wiki示例的截图

而读取时的转化方法如下图所示
UTF-16读取2或4个字节,并加以转换

UTF-16作为一个折衷方案,使常用字符都能用2个字节表示,这是其最大的优点。 而由于UTF-16使用了前导/后导代理,导致以11011开头的Unicode编码无法使用(会导致UTF-16格式下无法辨识),因此Unicode规定U+D800 - U+DFFF区间内的编码空置。

BOM(字节顺序标记)

由于多种字符转化格式的存在,一个文本文件通常需要在其开头注明该文件的格式。 这种文件开始处的标记就称为byte-order mark,BOM。 BOM的示例直接从wiki拉过来 https://en.wikipedia.org/wiki/Byte_order_mark

编码 表示(十六进制 表示(十进制
UTF-8 EF BB BF 239 187 191
UTF-16(大端序) FE FF 254 255
UTF-16(小端序) FF FE 255 254
UTF-32(大端序) 00 00 FE FF 0 0 254 255
UTF-32(小端序) FF FE 00 00 255 254 0 0
UTF-7 2B 2F 76
和以下的一个字节:
[ 38 \| 39 \| 2B \| 2F ]
43 47 118
和以下的一个字节:
[ 56 \| 57 \| 43 \| 47 \]
UTF-1 F7 64 4C 247 100 76
UTF-EBCDIC DD 73 66 73 221 115 102 115
Unicode标准压缩方案 0E FE FF 14 254 255
BOCU-1 FB EE 28 及可能跟随着FF 251 238 40 及可能跟随着255
GB-18030 84 31 95 33 132 49 149 51

以上为一个半路出家的码农,遍搜学习资料后加以自己的理解,整理成文