编辑说明:本文初稿完成后,通过 Claude Code 进行了技术审阅和优化润色,修正了 UTF-16 编码方式的描述,补充了 Unicode 平面结构、代理对机制、MySQL utf8mb4 等深度内容,使文章在保持通俗易懂的同时更加准确严谨。
字符编码详解:UTF-8、Unicode、GBK 等
一、核心概念:Unicode vs UTF-8
用快递系统来理解
Unicode(统一字符集)
- 相当于:全国统一的地址编号系统
- 作用:给世界上每个文字、符号分配唯一编号
- 例子:
- ‘A’ → U+0041(编号 65)
- ‘中’ → U+4E2D(编号 20013)
- ‘😊’ → U+1F60A(编号 128522)
- 特点:只定义编号,不管怎么存储
UTF-8(编码方式)
UTF-8 根据 Unicode 码点的大小,使用不同长度的字节:
| Unicode 范围 | 字节数 | UTF-8 格式 |
|---|---|---|
| U+0000 ~ U+007F | 1 字节 | 0xxxxxxx |
| U+0080 ~ U+07FF | 2 字节 | 110xxxxx 10xxxxxx |
| U+0800 ~ U+FFFF | 3 字节 | 1110xxxx 10xxxxxx 10xxxxxx |
| U+10000 ~ U+10FFFF | 4 字节 | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx |
- 相当于:快递打包和运输方式
- 作用:把 Unicode 编号转换成实际的字节数据
- 例子:
- ‘A’ (U+0041) →
01000001(1 字节) - ‘中’ (U+4E2D) →
11100100 10111000 10101101(3 字节) - ‘😊’ (U+1F60A) →
11110000 10011111 10011000 10001010(4 字节)
- ‘A’ (U+0041) →
- 特点:变长编码,节省空间
上面的例子详解:
固定的前缀:告诉解码器这是几字节的字符
0= 1 字节110= 2 字节1110= 3 字节11110= 4 字节
后续字节都以
10开头:便于识别和同步x 的位置:填入 Unicode 码点的二进制数据
例 1:’A’ (U+0041)
- Unicode: U+0041 = 十进制 65
- 范围判断:65 < 128,使用 1 字节
- 二进制:
01000001 - 套用格式
0xxxxxxx:直接就是01000001
例 2:’中’ (U+4E2D)
- Unicode: U+4E2D = 十六进制 4E2D = 二进制
0100111000101101 - 范围判断:4E2D 在 0800-FFFF 之间,使用 3 字节
- 格式:
1110xxxx 10xxxxxx 10xxxxxx(共 16 个 x)
填充步骤:
1 | 原始二进制:0100 111000 101101 |
例 3:’😊’ (U+1F60A)
- Unicode: U+1F60A = 二进制
000011111011000001010(21 位) - 范围判断:1F60A > 10000,使用 4 字节
- 格式:
11110xxx 10xxxxxx 10xxxxxx 10xxxxxx(共 21 个 x)
填充步骤:
1 | 原始二进制:000 011111 011000 001010 |
关系总结
1 | Unicode = 字典(定义每个字是什么编号) |
二、UTF-8 的优势
为什么 UTF-8 最流行?
节省空间
- 英文字母:1 字节(和 ASCII 一样)
- 中文汉字:3 字节(够用)
- 复杂符号:4 字节(罕见)
兼容 ASCII
- 前 128 个字符完全一样
- 老程序无需修改就能用
容错性好
- 损坏一个字节不会影响其他字符
- UTF-16/UTF-32 字节损坏后可能导致多个字符解析错误
三、其他 Unicode 编码方式
UTF-16
- 常用字符 2 字节,emoji 等特殊符号 4 字节
- 使用场景:Windows 内部、Java 字符串
- 优点:处理中文等字符效率较高
- 缺点:不兼容 ASCII,浪费空间
🔍 深入理解:为什么 UTF-16 有时 2 字节有时 4 字节?
这涉及 Unicode 的”平面”(Plane)概念:
Unicode 的分区结构
1 | Unicode 字符空间 = 17 个平面 × 每个平面 65536 个字符 |
1. BMP 基本多文种平面(Basic Multilingual Plane)
- 范围:U+0000 到 U+FFFF(0 到 65535)
- 包含内容:
- 英文字母、数字、标点
- 中日韩常用汉字(CJK)
- 阿拉伯文、西里尔文等各国文字
- 常用符号(©、®、™ 等)
- UTF-16 编码:直接用 2 字节表示
- 覆盖率:99% 的日常使用字符都在这里
2. 补充平面(Supplementary Planes)
- 范围:U+10000 到 U+10FFFF(65536 到 1,114,111)
- 包含内容:
- Emoji 表情:😊 🚀 🎉
- 罕见汉字:𠮷(吉的异体字)、𩸽(鱼名)
- 古代文字:𒀀(楔形文字)、𓀀(埃及圣书体)
- 数学符号:𝕏(双线体字母)
- UTF-16 编码:用”代理对”(Surrogate Pair)表示
什么是代理对?
因为补充平面的字符编号超过了 65535,2 字节装不下,UTF-16 用了一个巧妙方法:
1 | 代理对 = 高代理(High Surrogate)+ 低代理(Low Surrogate) |
实际例子:
1 | 字符:😊(笑脸 emoji) |
为什么这么设计?
- 当年设计 UTF-16 时,以为 65536 个字符够用了
- 后来发现不够(emoji、古文字不断增加)
- 为了向后兼容,只能用代理对这个”补丁”方案
UTF-32
- 固定 4 字节,直接存储 Unicode 码点值
- 使用场景:内部处理(很少用于存储)
- 优点:每个字符长度一致,索引方便
- 缺点:极度浪费空间
对比表
| 字符 | Unicode 编号 | UTF-8 | UTF-16 | UTF-32 |
|---|---|---|---|---|
| A | U+0041 | 1 字节 | 2 字节 | 4 字节 |
| 中 | U+4E2D | 3 字节 | 2 字节 | 4 字节 |
| 😊 | U+1F60A | 4 字节 | 4 字节 | 4 字节 |
说明:UTF-8 对英文友好,UTF-16 对中文友好,UTF-32 对所有字符等长但浪费空间
四、历史遗留:GBK、GB2312 等
背景故事
在 Unicode 普及之前,各国都有自己的编码标准:
- 中国:GB2312、GBK、GB18030
- 日本:Shift-JIS
- 韩国:EUC-KR
- 台湾:Big5
中文编码演进
1. GB2312(1980 年)
- 收录:6763 个汉字 + 682 个符号
- 范围:常用简体中文
- 编码:2 字节(定长)
- 缺点:繁体字、生僻字都没有
2. GBK(1995 年)
- 收录:21003 个汉字
- 范围:简体 + 繁体 + 生僻字
- 编码:2 字节(定长)
- 特点:向下兼容 GB2312
- 缺点:不是国际标准,只能表示中文
3. GB18030(2000 年)
- 收录:70244 个汉字
- 范围:中日韩统一汉字 + 少数民族文字
- 编码:1/2/4 字节(变长)
- 特点:国家强制标准,兼容 GBK
- 现状:逐渐被 UTF-8 取代
GBK vs UTF-8 对比
| 特性 | GBK | UTF-8 |
|---|---|---|
| 国际通用性 | ❌ 只用于中文 | ✅ 全球标准 |
| 中文效率 | ✅ 2 字节 | ⚠️ 3 字节 |
| 英文效率 | ❌ 2 字节 | ✅ 1 字节 |
| 兼容 ASCII | ❌ 不兼容 | ✅ 完全兼容 |
| emoji 支持 | ❌ 不支持 | ✅ 支持 |
五、实际应用场景
什么时候用 UTF-8?
- ✅ 网页开发(HTML/CSS/JS)
- ✅ 数据库存储(MySQL、PostgreSQL)
- ✅ API 接口(JSON/XML)
- ✅ 跨平台文件(Linux/Mac/Windows)
- ✅ 开源项目(国际协作)
什么时候可能遇到 GBK?
- ⚠️ 老旧 Windows 系统(中文版)
- ⚠️ 某些政府/银行系统
- ⚠️ 老式数据库导出文件
- ⚠️ 古老的文本文件
编码混乱的典型问题
乱码示例
1 | 原文:你好世界 |
原因:写入时用 UTF-8,读取时用 GBK(或反过来)
解决:统一使用 UTF-8,在文件头声明编码
1 | <!-- HTML --> |
1 | # Python |
六、快速判断指南
遇到乱码怎么办?
检查文件编码
1
2
3
4# Linux/Mac
file -i 文件名.txt
# 显示: charset=utf-8 或 charset=iso-8859-1转换编码
1
2# 将 GBK 转为 UTF-8
iconv -f GBK -t UTF-8 input.txt > output.txtPython 读取指定编码
1
2
3
4
5
6
7# 读取 GBK 文件
with open('file.txt', encoding='gbk') as f:
content = f.read()
# 保存为 UTF-8
with open('file_utf8.txt', 'w', encoding='utf-8') as f:
f.write(content)
七、记忆口诀
1 | Unicode 是身份证号,给每个字编号; |
八、常见误区
❌ 误区 1:”UTF-8 是一种字符集”
✅ 正确:UTF-8 是编码方式,Unicode 才是字符集
❌ 误区 2:”GBK 比 UTF-8 更省空间”
✅ 正确:仅对纯中文文本成立,混合英文时 UTF-8 更优
❌ 误区 3:”Unicode 只有 UTF-8 一种编码”
✅ 正确:还有 UTF-16、UTF-32 等多种编码
❌ 误区 4:”UTF-8 每个字符都是 8 位”
✅ 正确:UTF-8 是变长编码,1-4 字节不等
❌ 误区 5:”UTF-8 文件必须有 BOM”
✅ 正确:UTF-8 不需要 BOM(字节顺序标记),加上反而可能导致问题
九、推荐做法
新项目开发
一律使用 UTF-8
- 源代码文件:UTF-8
- 数据库:UTF-8(MySQL 使用
utf8mb4,因为 MySQL 的utf8是阉割版只支持最多 3 字节) - 网页:
<meta charset="UTF-8"> - API 响应头:
Content-Type: application/json; charset=utf-8
避免使用 GBK
- 除非对接古老系统必须用 GBK
- 转换时先转成 UTF-8 再处理
文本编辑器设置
- VS Code:设置默认编码为 UTF-8
总结
- Unicode:全球字符大字典,定义编号规则
- UTF-8:最流行的编码方式,节省空间、兼容好
- GBK/GB2312:历史遗留的中文编码,逐渐淘汰
- 建议:新项目全部用 UTF-8,老项目遇到 GBK 及时转换
最终建议:如果不知道用什么编码,就用 UTF-8,99% 的情况下没问题!
更新记录
- 2025-12-10:文章创建
- 2025-12-10:通过 Claude Code 进行技术审阅,主要优化:
- 修正 UTF-16 编码方式的描述(从”固定字节”改为基于 Unicode 平面的准确表述)
- 新增 Unicode 平面结构和代理对机制的深度解析
- 补充 MySQL
utf8mb4的详细说明 - 新增 BOM(字节顺序标记)相关误区
- 优化对比表说明和容错性表述
- 保持科普风格的同时提升技术准确性