下述内容大部分引用自CSDN

Unicode 是 unicode.org 制定的编码标准,目前得到了绝大部分操作系统和编程语言的支持。unicode.org 官方对 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个字节表示。

unicode.org 定义了百万个以上的字符,如果将所有的字符用统一的格式表示,需要的是 4 个字节。“a”的 Unicode 表示就会变成 0x00000061,而“一“的 Unicode 值是 0x00004E00。实际上,这就是 UTF32,Linux 操作系统上所使用的 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 个字节才能表示,还是有点浪费的。

05-28 08:09