字符集是字符的集合,常见的有:ASCII字符集、GB2312字符集、BIG5字符集、 GB18030字符集、Unicode字符集等
字符编码则是编码规则,是计算机用于解析字符的规则。常见的有ISO-8859-1,GB2312,GBK,UTF-8,UTF-16等。
本文讲讲 Unicode 字符集和它的常见编码格式。
Unicode
Unicode是国际组织制定的可以容纳世界上所有文字和符号的字符编码方案。用数字0-0x10FFFF来映射这些字符,最多可以容纳1114112个字符。UTF-8、UTF-16、UTF-32都是将数字转换到程序数据的编码方案。
Unicode的编码空间可以划分为17个平面(plane),每个平面包含65,536个码位。17个平面的码位可表示为从U+xx0000到U+xxFFFF。第一个平面称为基本多语言平面(Basic Multilingual Plane, BMP),或称第零平面(Plane 0),其他平面称为辅助平面(Supplementary Planes)。
在基本多语言平面内,从U+D800到U+DFFF之间的码位区段是空出来的码位。UTF-16利用这块码位来对辅助平面的字符进行编码。
UTF-8
UTF-8是一种可变长度字符编码。
现在看这样一句话:It's 知乎日报,在Unicode中对应的码和二进制分别是:
I 0049 00000000 01001001
t 0074 00000000 01110100
' 0027 00000000 00100111
s 0073 00000000 01110011
0020 00000000 00100000
知 77e5 01110111 11100101
乎 4e4e 01001110 01001110
日 65e5 01100101 11100101
报 62a5 01100010 10100101
可以看到,英文的字符前9位都是0,太浪费了,因为utf-8采用了可变长度编码,以8bits为最小变长。编码后如下:
I 01001001
t 01110100
' 00100111
s 01110011
00100000
知 11100111 10011111 10100101
乎 11100100 10111001 10001110
日 11100110 10010111 10100101
报 11100110 10001010 10100101
它的编码规则是这样的:
0000〜007F:0XXXXXXX
0080〜07FF:110XXXXX 10XXXXXX
0800〜FFFF:1110XXXX 10XXXXXX 10XXXXXX
10000〜10FFFF:11110XXX 10XXXXXX 10XXXXXX 10XXXXXX
可以看到utf8是一种前缀编码,编码成一个字节的首位为0,编码成两个字节的,第一个字节的前两位为1,编码为三个字节的,第一个字节前三为为1...就是通过这种手段,计算机知道接下来的一个字符包含多少个字节。
自2009年以来,UTF-8一直是万维网的最主要的编码形式。截止到2019年11月, 在所有网页中,UTF-8编码应用率高达94.3%。第二热门的多字节编码方式Shift JIS和GB 2312分别具有0.3%和0.2%的占有率。几乎所有的互联网联盟都建议采用UTF-8编码。
结构:
UTF-8原本使用1-6个字节为每个字符编码,但2003年11月UTF-8被RFC 3629重新规范,只能使用原来Unicode定义的区域,U+0000到U+10FFFF,也就是说最多四个字节。
- 128个US-ASCII字符只需一个字节编码(Unicode范围由U+0000至U+007F)。
- 带有附加符号的拉丁文、希腊文、西里尔字母、亚美尼亚语、希伯来文、阿拉伯文、叙利亚文及它拿字母则需要两个字节编码(Unicode范围由U+0080至U+07FF)。
- 其他基本多文种平面(BMP)中的字符(这包含了大部分常用字,如大部分的汉字)使用三个字节编码(Unicode范围由U+0800至U+FFFF)。
- 其他极少使用的Unicode 辅助平面的字符使用四字节编码。
特点:
Ascll码是它的子集,所有英文只需要一个字节,对于欧美地区而言非常的高效且便捷。但中文字符基本都需要三个字节,比较不方便。
UTF-16
utf-16也是一种长度的编码,每个字符占用两个字节或者四个字节。
-
对于基本平面字符,unicode码和utf-16相同,只占用两个字节。
-
对于辅助平面字符,被编码成一对码元,占用四个字节:
- 码位减去 0x10000, 则得到 20 bit 长的 0 - 0xFFFFF。
- 高位的10bit,加上 0xD800 得到第一个码元,又称高位或前导,值的范围是
0xD800...0xDBFF
- 低位的10bit,加上 0xDC00 得到第二个码元,又称低位或后尾,值的范围是
0xDC00...0xDFFF
这样设计,那么对于一个两个字节,很容易判断其是基本字符,还是辅助字符,而且很好判断是前导还是后尾。这样每次读取都是连续的 2 字节。
不同的操作系统对字节顺序的理解不一致。如某字符为十六进制编码4E59,按两个字节拆分为4E和59,在Mac上读取时是从低字节开始,那么会认为此4E59编码为594E,找到的字符为“奎”,而在Windows上从高字节开始读取,则编码为U+4E59的字符为“乙”。此类情况说明UTF-16的编码顺序若不加以人为定义就可能发生混淆,于是在UTF-16编码实现方式中使用了大端序(Big-Endian,简写为UTF-16 BE)、小端序(Little-Endian,简写为UTF-16 LE)的概念,以确保不同的字节序也能呈现相同内容,目前Windows和Linux默认使用UTF-16 LE。
utf-8 和 utf-16 如何选择
utf-16的特点是编码规整,方便cpu读写。因此在需要进行内存读写时,采用utf-16
但是utf-16对于ascll码字符存在空间浪费,而多数时候ascll码字符更多,因此在网络或者存储时,普遍采用utf-8.
另外,对于cjk(中日韩文字)编码时,很多字符都是在于基本平面内,使用 utf-16 只需要两字节,而使用 utf-8 通常需要 3 字节。也就是说,在cjk文字偏多时,采用utf-16更加有空间优势。
-
Java的编码格式是什么?
java的编码格式要分情况来看,在类文件中编码默认是 utf-8,可以手动指定;在 jvm 运行时,即在内存中,都转换为 utf-16。原因就是 utf-16 方便 cpu 读写,而utf-8对于ascll字符多的情况下更省内存。
UTF-32
utf-32是定长的编码,每个码位使用四个字节进行编码。优点是和unicode一一对应,缺点是太浪费空间。
参考:
https://zh.wikipedia.org/wiki/Unicode
https://zh.wikipedia.org/wiki/UTF-8
https://zh.wikipedia.org/wiki/UTF-16