UTF8、UTF16、UTF16-LE、UTF16-BE、UTF32都是些什么?下述内容⼤部分引⽤⾃:
Unicode 是制定的编码标准,⽬前得到了绝⼤部分操作系统和编程语⾔的⽀持。官⽅对 Unicode 的定义是:Unicode provides a unique number for every character, no matter what the platform, no matter what the program, no matter what the language。可见,Unicode 所做的是为每个字符定义了⼀个相应的数字表⽰。⽐如,“a”的 Unicode 值是 0x0061,“⼀”的 Unicode 值是 0x4E00,这是最简单的情况,每个字符⽤2个字节表⽰。
定义了百万个以上的字符,如果将所有的字符⽤统⼀的格式表⽰,需要的是 4 个字节。“a”的 Unicode 表⽰就会变成 0x00000061,⽽“⼀“的Unicode 值是 0x00004E00。实际上,这就是 UTF32,Linux 操作系统上所使⽤的 Unicode ⽅案。
unicode所有字符但是,仔细分析可以发现,其实绝⼤部分字符只使⽤ 2 个字节就可以表⽰了。英⽂的 Unicode 范围是 0x0000-0x007F,中⽂的 Unicode 范围是 0x4E00-0x9F**,真正需要扩展到 4 个字节来表⽰的字符少之⼜少,所以有些系统直接使⽤ 2 个字节来表⽰ Unicode。⽐如 Windows 系统上,Unicode 就是两个字节的。对于那些需要 4 个字节才能表⽰的字符,使⽤⼀种代理的⼿法来扩展(其实就是在低两个字节上做⼀个标记,表⽰这是⼀个代理,需要连接上随后的两个字节,才能组成⼀个字符)。这样的好处是⼤量的节约了存取空间,也提⾼了处理的速度。这种 Unicode 表⽰⽅法就是 UTF16。⼀般在 Windows 平台上,提到 Unicode,那就是指 UTF16 了。
⾄于 UTF16-LE 和 UTF16-BE,则与计算机的 CPU 构架有关。LE 指 Little Endian,⽽ BE 指 Big Endian。由于 UTF16 是双字节编码,所以两个字节保存时哪个在前,哪个在后关系到解析出字符的结果。⾄于为什么会出现 BE 和 LE 的编码,则是由于历史原因造成的:在 Mac 和 PC 机上,对字节顺序的理解是不⼀致的。如果⼀个⽂件不明确说明 UTF16 使⽤的是 BE 还是 LE,那么就需要通过 BOM 来指明了。我们⼀般的 X86 系统都是 Little Endian 的,可以认为 UTF16=UTF16-LE。
由于对于欧洲和北美,实际上使⽤的编码范围在 0x0000-0x00FF 之间,只需要⼀个字符就可以表⽰所有的字符。即使是使⽤ UTF16 来作为内存的存取⽅式,还是会带来巨⼤的空间浪费,因此就有了 UTF8 的编码⽅式。UTF8 是⼀个可变长度字符编码,它同时是⼀个前缀码,前缀码的特征是,编码系统中的任意⼀个合法的码不会是另外⼀个码的前缀,所以 UTF8 不需要指定字节序。⼀个 UTF8 编码可以⽤ 1~6 个字节来表⽰,将第⼀个字节的前⼏个⽐特设置为 1 来指定这个字符占⽤⼏个⽐特,⽐如⼀个两字节的字符的编码,第⼀位是 110xxxxx,第⼆位是 10xxxxxx,⽽⼀个六字节字符的编码是这样的:1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx,所以 UTF-8 最多能编码 231 个字符。所以对于只需要1个字节的字符,就使⽤⼀个字节。对于中⽇韩等原本需要两个字节才能表⽰的字符,则通过⼀个UTF16-UTF8 的算法实现相互之间的转换,⼀般需要 3 个字节才能表⽰。UTF8 使⽤的算法很有意思,⼤致映射关系如下:
Unicode编码UTF-8编码(⼆进制)
U+0000 – U+007F0xxxxxxx
U+0080 – U+07FF110xxxxx 10xxxxxx
U+0800 – U+FFFF1110xxxx 10xxxxxx 10xxxxxx
U+10000 – U+10FFFF11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
由于 UTF8 可以⽅便的转换为 UTF16 和 UTF32,⽽且 UTF8 在每个操作系统平台上的实现都是⼀样的,也不存在跨平台的问题,所以UTF8 成为跨平台的 Unicode 很好的解决⽅案。当然,对于中⽂来说,由于每个字符需要 3 个字节才能表⽰,还是有点浪费的。