2

UTF-8编码 - 真正的飞鱼

 1 year ago
source link: https://www.cnblogs.com/feiyu2/p/UTF-8.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

介绍 UTF-8 编码

UTF-8 是一种针对 Unicode 的可变长度字符编码。

针对 Unicode:UTF-8 是 Unicode 的实现方式之一。相当于 Unicode 规定了字符对应的代码值,这个代码值需要转换为字节序列的形式,用于数据存储、传输。代码值到字节序列的转换工作由 UTF-8 来完成。

可变长度字符编码:UTF-8 使用一至四个字节对 Unicode 字符集中的所有有效代码点进行编码。

  • UTF-8 使用 1 个字节表示 ASCII 字符;
  • UTF-8 使用 2 个字节表示带有附加符号的拉丁文、希腊文等;
  • UTF-8 使用 3 个字节表示其他基本多文种平面(BMP)中的字符(包含了大部分常用字,如大部分的汉字);
  • UTF-8 使用 4 个字节表示 Unicode 辅助平面的字符。

技术是为了解决问题而生的,UTF-8 编码是为了解决什么问题而设计的呢?UTF-8 是为了兼容 ASCII 编码而设计的。

ASCII 编码使用 1 个字节表示 ASCII 字符,而 Unicode 最初规定使用 2 个字节来表示所有的 Unicode 字符。如果使用 2 个字节来表示 ASCII 字符的话,那么含有大量 ASCII 字符的文本将浪费大量的存储空间。

UTF-8 编码使用 1 个字节来表示 ASCII 字符,而且字面与 ASCII 码的字面一一对应,这使得原来处理 ASCII 字符的软件无须或只须做少部分修改,即可继续使用。

UTF-8 编码的规则

Unicode 和 UTF-8 之间的转换关系表(x 字符表示码点占据的位)

码点的位数 码点起值 码点终值 Byte 1 Byte 2 Byte 3 Byte 4 Byte 5 Byte 6
7 U+0000 U+007F 1 0xxxxxxx
11 U+0080 U+07FF 2 110xxxxx 10xxxxxx
16 U+0800 U+FFFF 3 1110xxxx 10xxxxxx 10xxxxxx
21 U+10000 U+1FFFFF 4 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
26 U+200000 U+3FFFFFF 5 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
31 U+4000000 U+7FFFFFFF 6 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx

image-20230124102710527.png

UTF-8 编码的规则:

  • 在 ASCII 码范围内的代码点,UTF-8 使用 1 个字节表示。
  • 大于 ASCII 码范围的代码点,UTF-8 使用多个字节表示。UTF-8 使用第一个字节的前几位表示该 Unicode 字符的字节长度(第一个字节的开头 1 的数目就是该 Unicode 字符的字节长度),其余字节的前两位固定为 10,作为标记
    • 如果第一个字节的前两位为 1,第三位为 0(110xxxxx),则表示 UTF-8 使用 2 个字节表示该 Unicode 字符;
    • 如果第一个字节的前三位为 1,第四位为 0(1110xxxx),则表示 UTF-8 使用 3 个字节表示该 Unicode 字符;
    • 依此类推;
    • 如果第一个字节的前六位为 1,第七位为 0(1111110x),则表示 UTF-8 使用 6 个字节表示该 Unicode 字符;

UTF-8 编码的字节含义:对于 UTF-8 编码中的任意字节 B:

  • 如果 B 的第一位为 0(0xxxxxxx),则 B 独立的表示一个 ASCII 字符;
  • 如果 B 的第一位为 1,第二位为 0(10xxxxxx),则 B 为一个多字节表示的字符中的一个字节;
  • 如果 B 的前二 / 三 / 四 / 五 / 六位为 1,其余位为 0,则 B 为二 / 三 / 四 / 五 / 六个字节表示的字符中的第一个字节。

UTF-8 编码示例

Unicode/UTF-8-character table (utf8-chartable.de)

image-20230124160453248.png

通过 UTF-8 编码表,我们可以看到中文字符 “一” 的 Unicode 代码点为 "U+4E00",UTF-8 编码结果为 "e4 b8 80",

对中文字符 “一” 进行 UTF-8 编码,是如何得到 "e4 b8 80" 的呢?我们下面来看。


"4E00" 的二进制表示为 "0100 1110 0000 0000"。

UTF-8 使用 3 个字节表示常用的汉字,因此中文字符对应的字节序列格式为:"1110xxxx 10xxxxxx 10xxxxxx"

于是中文字符 “一” 的 UTF-8 编码结果为 "11100100 10111000 10000000",它的十六进制表示为 "e4 b8 80"

public static void main(String[] args) throws UnsupportedEncodingException {
    byte[] bytes = "一".getBytes("UTF-8");
    // [-28, -72, -128]
    System.out.println(Arrays.toString(bytes));
}

UTF-8 编码的优劣局限

UTF-8 编码的优点

UTF-8 和 ASCII 兼容:ASCII 是 UTF-8 的一个子集。因为一个纯 ASCII 字符串也是一个合法的 UTF-8 字符串,所以现存的 ASCII 文本不需要转换。为传统的扩展 ASCII 字符集设计的软件通常可以不经修改或很少修改就能与 UTF-8 一起使用。

任何面向字节的字符串搜索算法都可以用于 UTF-8 的数据(只要输入仅由完整的 UTF-8 字符组成)。UTF-8 可以保证一个字符的字节序列不会包含在另一个字符的字节序列中。而有些比较旧的可变长度字符编码(如Shift JIS)没有这个特质,故它们的字符串搜索算法变得相当复杂。

UTF-8 字符串可以由一个简单的算法可靠地识别出来。由于 UTF-8 字节序列的设计,如果一个疑似为字符串的序列被验证为 UTF-8 编码,那么我们可以有把握地说它是 UTF-8 字符串。一个字符串在任何其它编码中表现为合法的 UTF-8 的可能性很低,可能性随着字符串长度的增长而减小。 举例说明,字符值 C0、C1、F5 至 FF 从来没有出现。为了更好的可靠性,可以使用正则表达式来统计非法过长和替代值(可以查看W3 FAQ: Multilingual Forms上的验证 UTF-8 字符串的正则表达式)。

UTF-8 编码可以通过屏蔽位 和 移位操作快速读写:屏蔽位是指将字节的高位置零,以便获取低位的值;移位操作是指将字节的低位移动到高位,以便获取高位的值。这样,可以快速读取和写入 UTF-8 编码的字符。

UTF-8 编码的缺点

UTF-8 编码不利于使用正则表达式进行读音检索

正则表达式可以进行很多高级的英文模糊检索。比如,[a-h] 表示 a 到 h 间的所有字母。

同样 GBK 编码的中文也可以这样利用正则表达式,比如在只知道一个字的读音而不知道怎么写的情况下,也可用正则表达式检索,因为 GBK 编码是按读音排序的。虽然正则表达式检索并未考虑中文的多音字,但是由于中文的多音字数量不多,不少多音字还是同音不同调类型的多音字,所以大多数情况下正则表达式检索是还可以接受的。

但是 Unicode 汉字不是按读音排序的,它是按部首排序,所以不利于用正则表达式进行读音检索。在只知道一个字的部首而不知道如何发音的情况下,UTF-8 可用正则表达式检索而 GBK 不行。


UTF-8 的 ASCII 字符只占用一个字节,比较节省空间,但是更多字符的 UTF-8 编码占用的空间就要多出1/2,特别是中文、日文和韩文(CJK)这样的方块文字,它们大多需要三个字节。

无法根据 Unicode 字符数判断出 UTF-8 文本占用的字节数。因为 UTF-8 是一种可变长度字符编码。

UTF-8 - 维基百科,自由的百科全书 (wikipedia.org)

Unicode/UTF-8-character table (utf8-chartable.de)

__EOF__


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK