当前位置: 首页 > news >正文

【Java】UTF-8变长编码及其3字节存储奥秘

UTF-8 是一种变长编码,一个字符可能由 1 到 4 个字节组成。

解码时(将字节数组转回 String),计算机并不需要“猜”或者去查表,因为长度信息本身就包含在字节的“头部”里。这就是 UTF-8 设计的精妙之处:它是“自同步”的。

核心机制:看字节的“高位”标志

计算机读取字节时,是按比特(Bit)一位一位看的。UTF-8 规定,利用每个字节的前几位(高位)来告诉解码器:“这个字节是独立字符,还是某个字符的一部分”。

这就好比我们看车牌号,如果第一个字母是“京”,我们就知道这车是北京的;如果第一个字母是“豫”,就知道是河南的。UTF-8 的字节也是通过“长相”来区分的。

具体的编码规则表

让我们看看一个字节(8个比特)的二进制表示。x代表存储数据的位,01是标志位:

字节数格式说明数据位数量理论最大十进制数实际 UNICODE 上限
1 字节0xxxxxxx0开头,这是 ASCII 字符(0-127)7 位127127
2 字节110xxxxx 10xxxxxx110开头,后面跟 1 个字节11 位2,0472,047
3 字节1110xxxx 10xxxxxx 10xxxxxx1110开头,后面跟 2 个字节16 位65,53565,535
4 字节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 码点是U+4E2D,在 UTF-8 中,它需要 3 个字节来存储:

字节数组:[228, 184, 173](十六进制0xE4 0xB8 0xAD

解码流程:

  1. 读取0xE411100100
    • 看到1110
    • 判决:这是 3 字节头,往后读 2 个。
  2. 读取0xB810111000
    • 看到10
    • 判决:是第 1 个后续字节,继续。
  3. 读取0xAD10101101
    • 看到10
    • 判决:是第 2 个后续字节。
    • 结束:凑齐了 3 个字节,成功解析出“中”字。

解码器看到1110...就知道往后数 2 个字节,把这三个字节里的x部分拼在一起,就还原出了“中”字。

总结

解码过程之所以准确,是因为 UTF-8 利用了二进制的高位作为标志位:

  • 如果读到一个字节,首位是0→ 1 字符,1 字节。
  • 如果读到一个字节,首位是110→ 1 字符,2 字节。
  • 如果读到一个字节,首位是1110→ 1 字符,3 字节。
  • 如果读到一个字节,首位是10→ 这不是头,这是尾巴(跟随字节)。

这种设计非常巧妙,既兼容了古老的 ASCII(英文只用 1 个字节,首位为 0),又支持了全世界所有的字符(用变长字节),而且解码速度快,不需要额外的索引表。


UTF-8:3字节存储原理

举例:汉字“中”的 Unicode 码点是U+4E2D(十进制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-8)1110xxxx 10xxxxxx 10xxxxxx

填充过程

从原始数据的低位开始,依次填入模板的x中(从后往前填):

  1. 取最后 6 位:101101→ 填入第 3 个字节 →101011010xAD
  2. 再取中间 6 位:111000→ 填入第 2 个字节 →101110000xB8
  3. 剩下的高位:0100→ 填入第 1 个字节 →111001000xE4

最终结果:

11100100 10111000 10101101

即十六进制的:E4 B8 AD

这就是为什么“中”字在 UTF-8 中占据了 3 个字节。

4.总结

编码方式存储机制“中”字占用大小原因
UTF-16(Java char)定长(大部分),直接存储数值2 字节数值 20013 小于 65535,直接存入 16 位空间。
UTF-8变长,需要前缀位标记3 字节UTF-8 的 2 字节模式最大只能存 2047。为了容纳 20013,必须用 3 字节模式。

一句话概括:

虽然 2 个字节的盒子(容量)足够装下 20013 这个数字,但 UTF-8 的 2 字节格式(包装方式)太小了(只能装到 2047),所以被迫换用了更大的 3 字节包装。

http://www.cnnetsun.cn/news/1506452.html

相关文章:

  • 零代码玩转OpenClaw:百川2-13B-4bits量化模型自动化办公入门
  • 嵌入式硬件第五弹——ARM(2)
  • OpenClaw语音交互扩展:nanobot镜像接入Whisper实现语音控制
  • 跨端 UI 设计统一:多平台的视觉和谐
  • OpenClaw跨平台实战:Windows到Mac的Qwen3-32B配置迁移
  • 南医三院脊柱骨折诊疗:微创技术与专科方案解析
  • 废弃电脑改造计划:OpenClaw+GLM-4-7-Flash搭建24/7自动化终端
  • window环境使用git-filter-repo
  • OpenClaw+GLM-4.7-Flash:自动化社交媒体管理
  • 200KB轻量级HTML编辑器:KindEditor如何让网页内容创作变得简单高效?
  • Simple Runtime Window Editor:突破窗口分辨率限制的技术实现与应用指南
  • 4太5也5与4y
  • macOS Ventura 13命令行自动化管理:定时开机与关机高效攻略
  • 在QEMU中定制AST2600设备:从Machine定义到硬件模拟实战
  • 如何高效使用LeaguePrank:英雄联盟个性化展示的终极指南 [特殊字符]
  • 跨平台文件同步:OpenClaw+Qwen3.5-9B智能处理NAS文档归类
  • 横向对比:@zxing/library vs html5-qrcode,你的Web扫码方案该选谁?
  • [FLAC无损下载]技术突破:网易云音乐资源高效获取的架构解析与工程实践
  • OpenClaw多模型切换实战:百川2-13B-4bits与Qwen3-32B混合调用策略
  • 【工程心法】砸碎 Keil 的黑盒枷锁!告别 IDE 农耕时代,用 CMake + GCC 构筑嵌入式工业级自动化构建矩阵
  • OpenClaw学习助手实战:Qwen3.5-9B自动整理PDF笔记与生成思维导图
  • [LangGraph编译原理]从“状态图(StateGraph)”到“Actor模型(Pregel)”的转变
  • 4步焕新计划:老旧Mac设备升级macOS全攻略
  • 告别复杂配置!5分钟掌握OCAT:OpenCore图形化配置神器
  • 大气层系统技术指南:从需求分析到进阶拓展
  • 淄博养老服务中心哪家推荐
  • 如何快速掌握开源歌词工具:LyricsX 3步高效配置终极指南
  • 基于ZLMediaKit API的Java流媒体服务实战:从配置到核心功能封装
  • 5个超实用的小型分类数据集推荐:从Tiny ImageNet到花卉识别(附下载链接)
  • Source Han Serif CN:多场景字体解决方案的技术实践与价值挖掘