简介
我如何知道要读取字符的字节数?
如果第一个字节的第一个位是 0,则它是单个字节。
如果第一个字节的前 3 位是 110,那么它有 2 个字节。
如果第一个字节的前 4 位是 1110,那么它有 3 个字节。
......
如果第一个字节的前 7 位为 1111110,则它有 6 个字节。
为什么字节 2 到 6 的前 2 位总是以 10 开头?
它用作标记,它指示这些字节是正在读取的字符的延续。没有这个,就无法区分这是一个新角色还是延续。
为了表示 2 个字节,为什么不在第一个字节中使用 11 而不是 110?
让我们假设我们没有 0,而是只有 11。如果第三位恰好是 1,我们如何将其与同样具有 111 的 3 字节的指示符区分开来。因此,我们需要有 0。
1 字符编码的统一:unicode 和 utf8
统一码又被称之为万国码 unicode,在1994年发布。此时人类终于有了收纳全部人造字符的统一字符集。
Unicode是Universal Multiple-Octet Coded Character Set 的缩写。 中文意思是 通用多字节编码字符集。
它是由一个名为Unicode学会,Unicode Consortium的机构制定的字符集系统。
Unicode字符集致力于为全世界线程每个语言的字符分配统一且唯一的字符编号,以满足跨语言,跨平台进行文本数据交换,处理,存储和显示。
码点/序号 字符
U+0000 .....
...
U+4E2D 中
...
U+10FFFF ...
序号是全世界所有语言文字的符号分配的唯一编号。序号的范围从 0x000000 到0x10FFFF,全部可以容纳110多万个字符。
这个序号也被称之为Unicode码点。第二列字符就称为Unicode字符。
考虑到目前使用最多也就是最基础的ASCII字符集码点兼容性。前128个码点对应ASCII字符集。
现在Unicode字符集的码点表有了,只需要知道每个码点在计算机的内存编码表示,位模式。
我们知道ASCII字符集的内存编码表示位模式使用的是与其字符码点相同的数值,那么Unicode采用的是上面内存编码表示呢?
答案是并不唯一,常用的有三类:
* UTF-16
该方案使用2或4表示每个Unicode字符码点。它的优点是编码解码简单,因为全部字符都用偶数字节表示;
其不足也很明显,比如存在字节序问题,不兼容ASCII字符内存表示以及空间效率不佳。
* UTF-32
该方案固定使用4字节表示每个Unicode字符码点。它的优点也是编解码简单,因为全部字符都用4字节表示。
不足也和UTF-16一样明显,同样存在字节序的问题。 不兼容ASCII字符内存表示,且空间效率是这三个方案最差的。
*UTF-8
以其他的不同,UTF8使用变长字节对Unicode字符码点进行编码。
编码采用字节数量与Unicode字符在码点表的序号有关。
表示序号(码点)小的字符使用的字节少,表示序号(码点)大的字符使用的字节多。
2 理解UTF8编码
UTF8编码使用的字节从1到4不等。前128个与ASCII字符重合码点(U+0000 ~U+007F)使用1字节表示;
带变音符号的拉丁文,希腊文,西里尔阿拉伯文使用2字节表示,函数使用3字节表示,其他极少使用的语言使用4字节表示。
这样的编码方案是兼容ASCII字符内存表示的,采用UTF-8方案在内存表示Unicode字符时,已有的ASCII字符可以直接被作为Unicode字符进行存储和传输,无需改变。
此外,UTF8的编码单元为1字节,也就是一次编解码1字节,所以在处理UTF8方案表示的Unicode字符就不需要像UTF16或UTF32那样考虑字节序问题。
这三个方案中,UTF8方案空间效率最高。
我们分别使用这三个编码方式对 Unicode的字符 A 编码,LE表示小端字节库 Little Endian,BE 表示大端字节序 Big Endian
Unicode A
Unicode 码点序号 0x000041
UTF-8编码: 0x41
UTF-16BE编码: 0xFEFF0041
UTF-16LE编码: 0xFFFE4100
UTF-32BE编码: 0x0000FEFF00000041
UTF-32LE编码: 0xFFFE000041000000
由于UTF-16和UTF-32编码方案存在字节序问题,因此上面针对每个方案各自给出两个结果。
以UTF-16小端字节序结果0xFFFE4100为例,这个编码结果由4字节组成,前两个字节为0xFEFE,这个特定位模式时字节序标记(Byte Order Mark,BOM)。
Unicode规范对字节序标记约定如下:
FF FE UTF-16 小端字节序
FE FF UTF-16 大端字节序
FF FE 00 00 UTF-32 小端字节序
00 00 FE FF UTF-32 大端字节序
EF BB BF UTF-8
如果没有提供字节序标记,则默认采用大端字节序解码。
另外我们注意到,Unicode 规范为UTF-8也准备了一个字节序标记 EF BB BF
但是由于UTF-8 没有字节序问题,因此这个BOM只是用于表明该数据流采用的为UTF-8编码方案,算是一个编码方案类型标记。
UTF-8编码方案由于优点众多,经过多年发展,已成为Unicode 字符在计算机中内存编码表示 位模式方案的事实标准。 Go运用顺应了这个趋势。源码文件的字符编码也是使用UTF8编码。
3 例子:在WEB编码中Content-Type 和 Meta 标记
在 Internet 中,为了使客户端和服务器正确通信,需要交换所使用的编码类型。
HTTP 协议有一个名为 content-type 的标头,可用于此目的。
内容类型:text/plain; charset=“UTF-8”。
如果 Web 服务器只处理一种类型的应用程序并且所有页面都使用相同的编码,则此操作正常。
如果 Web 服务器使用不同的编码处理多个应用程序,该怎么办?
HTML 有元标记,我们可以在这个标记中指定编码信息。
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
4 Unicode 和 UTF-8相互转换
Unicode是一种在计算机上使用的字符编码。
它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求。
本质上Unicode表示了一种字符与二进制编码的一一对应关系,所以是一种单字符的编码。
对于ASCII(不包含扩展128+)字符,UTF-8编码、Unicode编码、ASCII码均相同(即单字节以0开头)
对于非ASCII(不包含扩展128+)字符,若字符有n个字节(编码后)。
则首字节的开头为n个1和1个0,其余字节均以10开头。除去这些开头固定位,其余位组合表示Unicode字符。
转换(2+字节UTF-8)
UTF-8 to Unicode
将UTF-8 按字节进行分割,以编码规则去掉每个字节头部的占位01,剩下位进行组合即Unicode字符
Unicode to UTF-8
从低位开始每次取6位,前加10组成尾部一个字节。
直到不足六位,加上对应的n个1和1个0,首字节的大端不足位补0,
如补充字节后位数不够则再增加一字节,规则同上。
(按规则预估字节数,优先写好每个字节的填充位,从末端补充即可)
Go默认字符集Unicode以及采用编码方案UTF8,深入理解字符,字符集的属性,码点和内存编码表示以及它们之间的关系。
5 小结
Unicode是全球字符统一编码标准,包含110万个字符。
它有多种编码实现,如UTF-8、UTF-16和UTF-32。UTF-8现为事实标准,兼容ASCII,使用1-4字节编码字符,小字符用字节少,大字符用字节多。
UTF-16和UTF-32则固定2或4字节,但有字节序问题。
BOM用于标识字节序,但在UTF-8中仅作识别编码类型之用。Go语言源码默认采用UTF-8编码