背景
关于字符编码,强烈推荐阮一峰老师的文章《字符编码笔记:ASCII,Unicode 和 UTF-8》http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html
阮老师是从字符编码的发展历程角度阐述,简单讲,美国人在上个世纪60年代规范了ASCII 码(看其全称,American Standard Code for Information Interchange,基于电报码,电报码基于摩斯码),一个字节(第一位为0)编码了128个字符,Enough for English。
之后,各个国家就在这个基础上,使用第一位对字符集进行扩展。中国的gb家族,如gb2312,使用了两个字节,收录汉字六千多。但毕竟我汉字博大精深,据说8万多(常用三千多而已),gb18030用了4个字节,收了七万多。反正各国编码就是一个字 — 乱。
Unicode解决了这个问题(Wikipedia:多年筹备规划之后,The Unicode Consortium was incorporated in California on January 3, 1991, and in October 1991, the first volume of the Unicode standard was published.),每一个符号都给予一个独一无二的编码,可容纳百万多个符号(wikipedia:Unicode 11.0, contains a repertoire of 137,439 characters )。按理来说Unicode一统江湖,各门派可以安枕无忧了,不过问题随之而来了,有些字符需要四个字节来存储,而ASCII 码一个字节就够了,都用4个字节那就太浪费了(UTF-32的编码方式,一一对应),如何更灵活地处理这个问题呢?那就必须要考虑Unicode的其它编码实现方式了。
UTF-8 就是在互联网上使用最广的一种 Unicode 的编码实现方式(wikipedia: By October 2018 accounts for 92.5% of all web pages)。UTF-8 采用可变长的编码方式,使用1~4个字节表示一个符号,具体根据符号对应的Unicode编码长度而调整实现的字节长度。
关于Unicode及其实现方式还可参考阮老师的另一篇文章《Unicode与JavaScript详解》http://www.ruanyifeng.com/blog/2014/12/unicode.html,其中阐述了JS支持的UCS-2。
UTF-8
此处很多引用于Wikipedia UTF-8词条https://en.wikipedia.org/wiki/UTF-8
UTF-8是一种变长的编码方法(Wikipedia: originally designed by Ken Thompson and Rob Pike),字符长度从1个字节到4个字节不等。越是常用的字符,字节越短,最前面的128个字符,只使用1个字节表示,与ASCII码完全相同。
编号范围 | 字节 |
---|---|
0x0000 - 0x007F | 1 |
0x0080 - 0x07FF | 2 |
0x0800 - 0xFFFF | 3 |
0x010000 - 0x10FFFF | 4 |
具体如何编码呢?
UTF-8 的编码规则很简单,只有二条:
1)对于单字节的符号,字节的第一位设为0,后面7位为这个符号的 Unicode 码。因此对于英语字母,UTF-8 编码和 ASCII 码是相同的。
2)对于n字节的符号(n > 1),第一个字节的前n位都设为1,第n + 1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的 Unicode 码。
Char. number range (hexadecimal) | UTF-8 octet sequence (binary) |
---|---|
0000 0000 - 0000 007F | 0xxxxxxx |
0000 0080 - 0000 07FF | 110xxxxx 10xxxxxx |
0000 0800 - 0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx |
0001 0000 - 0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx |
那么除了标识位,真正可以用来编码的位数为8-(n+1)+6*(n-1)=1+5n(1<n<=4时),7(n=1时)。简单讲1、2、3、4个字节可用于有效编码的位数分别为7、11、16、21。
因此,“严”的 Unicode 是4E25(100111000100101),15个有效位,必须用三个字节编码为11100100 10111000 10100101,转换为16进制E4B8A5。
wikipedia上的这个图最清楚
Num of bytes | Bits for code point | First code point | Last code point | Byte 1 | Byte 2 | Byte 3 | Byte 4 |
---|---|---|---|---|---|---|---|
1 | 7 | U+0000 | U+007F | 0xxxxxxx | |||
2 | 11 | U+0080 | U+07FF | 110xxxxx | 10xxxxxx | ||
3 | 16 | U+0800 | U+FFFF | 1110xxxx | 10xxxxxx | 10xxxxxx | |
4 | 21 | U+10000 | U+10FFFF | 11110xxx | 10xxxxxx | 10xxxxxx | 10xxxxxx |
下图说明字符编码之间的转换
Character | Unicode | Octal code point | Binary code point | Binary UTF-8 | Octal UTF-8 | Hexadecimal UTF-8 |
---|---|---|---|---|---|---|
$ | U+0024 | 044 | 010 0100 | 00100100 | 044 | 24 |
¢ | U+00A2 | 0242 | 000 1010 0010 | 11000010 10100010 | 302 242 | C2 A2 |
ह | U+0939 | 004471 | 0000 1001 0011 1001 | 11100000 10100100 10111001 | 340 224 239 | E0 A4 B9 |
€ | U+20AC | 020254 | 0010 0000 1010 1100 | 11100010 10000010 10101100 | 342 202 254 | E2 82 AC |
? | U+10348 | 0201510 | 0 0001 0000 0011 0100 1000 | 11110000 10010000 10001101 10001000 | 360 220 215 210 | F0 90 8D 88 |