【Java】UTF-8变长编码及其3字节存储奥秘
UTF-8 是一种变长编码一个字符可能由 1 到 4 个字节组成。解码时将字节数组转回 String计算机并不需要“猜”或者去查表因为长度信息本身就包含在字节的“头部”里。这就是 UTF-8 设计的精妙之处它是“自同步”的。核心机制看字节的“高位”标志计算机读取字节时是按比特Bit一位一位看的。UTF-8 规定利用每个字节的前几位高位来告诉解码器“这个字节是独立字符还是某个字符的一部分”。这就好比我们看车牌号如果第一个字母是“京”我们就知道这车是北京的如果第一个字母是“豫”就知道是河南的。UTF-8 的字节也是通过“长相”来区分的。具体的编码规则表让我们看看一个字节8个比特的二进制表示。x代表存储数据的位0和1是标志位字节数格式说明数据位数量理论最大十进制数实际 UNICODE 上限1 字节0xxxxxxx0开头这是 ASCII 字符0-1277 位1271272 字节110xxxxx 10xxxxxx110开头后面跟 1 个字节11 位2,0472,0473 字节1110xxxx 10xxxxxx 10xxxxxx1110开头后面跟 2 个字节16 位65,53565,5354 字节11110xxx 10.. 10.. 10..11110开头后面跟 3 个字节21 位2,097,1511,114,111⚠️特别注意Unicode 标准的限制虽然 UTF-8 的 4 字节编码规则允许存到 2,097,151但实际上Unicode 标准本身并没有用完这个空间。Unicode 标准目前规定的最大码点是 1,114,111十六进制0x10FFFF。原因为了保持与 UTF-16 等其他编码的兼容性Unicode 标准限定了范围。结论在现实世界的计算机系统中有效的 UTF-8 4 字节字符最大只能到 1,114111。超过这个数值的二进制组合即使符合 UTF-8 的 4 字节格式被认为是“非法码点”不会被标准系统使用。解码器是如何工作的举个例子汉字“中”汉字“中”的 Unicode 码点是U4E2D在 UTF-8 中它需要 3 个字节来存储字节数组[228, 184, 173]十六进制0xE4 0xB8 0xAD解码流程读取0xE411100100看到1110判决这是 3 字节头往后读 2 个。读取0xB810111000看到10判决是第 1 个后续字节继续。读取0xAD10101101看到10判决是第 2 个后续字节。结束凑齐了 3 个字节成功解析出“中”字。解码器看到1110...就知道往后数 2 个字节把这三个字节里的x部分拼在一起就还原出了“中”字。总结解码过程之所以准确是因为 UTF-8 利用了二进制的高位作为标志位如果读到一个字节首位是0→ 1 字符1 字节。如果读到一个字节首位是110→ 1 字符2 字节。如果读到一个字节首位是1110→ 1 字符3 字节。如果读到一个字节首位是10→ 这不是头这是尾巴跟随字节。这种设计非常巧妙既兼容了古老的 ASCII英文只用 1 个字节首位为 0又支持了全世界所有的字符用变长字节而且解码速度快不需要额外的索引表。UTF-83字节存储原理举例汉字“中”的 Unicode 码点是U4E2D十进制20013在 UTF-8 中它需要 3 个字节来存储字节数组[228, 184, 173]十六进制0xE4 0xB8 0xAD。在 Java 内部char类型是使用 2 个字节UTF-16 编码来存储“中”这个字的。但是UTF-8 之所以需要 3 个字节是因为 UTF-8 的编码规则为了兼顾兼容性和可读性牺牲了一定的存储空间。下面详细解释为什么明明 2 个字节够用UTF-8 却非要用 3 个字节。1.数值上的可行性2 字节确实够“中”字的 Unicode 码点是20013十进制。我们把它转换成二进制20013 0100 1110 0010 1101这串二进制一共有 15 位。而 2 个字节等于 16 位。因为 15 16所以单纯存这个数字2 个字节绰绰有余。Java 内部存储UTF-16直接把这 15 位二进制填入 2 个字节中高位补 0即0x4E2D。这就是为什么 Java 的char只需要 2 个字节。2.为什么 UTF-8 需要 3 个字节UTF-8 是一种变长编码。它的设计目标之一是兼容 ASCII 码0-127并且不需要像 UTF-16 那样面临“字节序”大端小端的问题。为了实现这一点UTF-8 规定了非常严格的位格式。它不像 UTF-16 那样直接把数字填进去而是把二进制位“拆分”到不同的字节容器里每个字节都要留出几位作为“位标记”告诉计算机这个字节是几字节序列的一部分。UTF-8 的位规则1 字节0 - 127格式0xxxxxxx最高位是 0剩下 7 位存数据。2 字节128 - 2047格式110xxxxx 10xxxxxx。第一个字节的前 3 位必须是110。后续字节的前 2 位必须是10。剩下的位才是存数据的5 6 11 位。2 字节 UTF-8 能存的最大数值是 2¹¹ - 1 2047。3 字节2048 - 65535格式1110xxxx 10xxxxxxx 10xxxxxx。第一个字节的前 4 位1110。后续字节的前 2 位10。剩下的位存数据4 6 6 16位。3 字节 UTF-8 能存的最大数值是 2¹⁶ - 1 65535。关键点来了“中”字的数值是 20013。而 2 字节 UTF-8 能存储的最大数值只有 2047。因为20013 2047所以“中”字装不进 2 字节的 UTF-8 格式里必须升级到 3 字节的格式。3.实际转换演示“中”字在 UTF-8 里具体是怎么变成 3 个字节的原始数据二进制0100 1110 0010 1101共 15 位目标模板3字节 UTF-81110xxxx 10xxxxxx 10xxxxxx填充过程从原始数据的低位开始依次填入模板的x中从后往前填取最后 6 位101101→ 填入第 3 个字节 →101011010xAD再取中间 6 位111000→ 填入第 2 个字节 →101110000xB8剩下的高位0100→ 填入第 1 个字节 →111001000xE4最终结果11100100 10111000 10101101即十六进制的E4 B8 AD这就是为什么“中”字在 UTF-8 中占据了 3 个字节。4.总结编码方式存储机制“中”字占用大小原因UTF-16Java char定长大部分直接存储数值2 字节数值 20013 小于 65535直接存入 16 位空间。UTF-8变长需要前缀位标记3 字节UTF-8 的 2 字节模式最大只能存 2047。为了容纳 20013必须用 3 字节模式。一句话概括虽然 2 个字节的盒子容量足够装下 20013 这个数字但 UTF-8 的 2 字节格式包装方式太小了只能装到 2047所以被迫换用了更大的 3 字节包装。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2453275.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!