Unicode 以及 UTF-8 编码
1、Unicode
Unicode(统一码 / 万国码)是一个国际标准字符编码系统,它的目标是为世界上所有文字、符号和表情分配一个唯一的数字编号(称为“码点”,Code Point),从而让计算机能够一致地表示、处理和交换各种语言的文本。
🌍 为什么需要 Unicode?
在 Unicode 出现之前,不同地区使用不同的编码标准,比如:
- 英语:ASCII(只支持 128 个字符)
- 中文:GB2312、GBK、Big5
- 日文:Shift-JIS
- 俄语:KOI8-R、Windows-1251
- ……
这些编码互不兼容,同一个字节序列在不同系统中可能显示成完全不同的字符,导致乱码。
👉 Unicode 的出现就是为了解决“乱码”问题,实现“一种编码,通吃全球”。
🔢 Unicode 的核心思想
给每个字符一个唯一的编号(码点),格式为 U+XXXX(十六进制)。
例如:
A→ U+0041中→ U+4E2D€(欧元符号)→ U+20AC🙂(微笑表情)→ U+1F642-
目前 Unicode 收录了超过 15 万个字符,涵盖:
- 拉丁字母、汉字、阿拉伯文、梵文、希伯来文……
- 数学符号、标点、表情符号(Emoji)、象形文字等
⚠️ 注意:Unicode ≠ 编码
很多人容易混淆:
- Unicode 是字符集(Character Set):它只定义“哪个数字对应哪个字符”。
- UTF-8、UTF-16、UTF-32 才是编码方式(Encoding):它们规定“如何把 Unicode 码点转换成字节存储或传输”。
✅ Unicode 的优势
| 优点 | 说明 |
|---|---|
| 统一性 | 一套编码支持所有语言 |
| 无歧义 | U+4E2D 永远是“中”,不会变成别的字 |
| 可扩展 | 不断新增字符(如每年新增 Emoji) |
| 现代标准 | 被 HTML、XML、JSON、操作系统、编程语言广泛采用 |
💡 小知识
- Unicode 最大支持码点到 U+10FFFF(共 1,114,112 个位置)。
- 常用汉字基本在 U+4E00 ~ U+9FFF(“CJK 统一汉字”区块)。
- 表情符号(Emoji)大多位于 U+1F600 ~ U+1F64F 等区域。
总结一句话:
Unicode 是让全世界的文字都能在计算机里“和平共处”的统一编码标准。
而 UTF-8 是目前最流行的 Unicode 实现方式,也是互联网的默认编码(占全球网页的 98% 以上)。
2、UTF-8
UTF-8(8-bit Unicode Transformation Format)是一种可变长度的字符编码方式,用于表示 Unicode 字符集中的所有字符。它是目前互联网和操作系统中最广泛使用的文本编码格式。
UTF-8 是 Unicode Transformation Format - 8-bit 的缩写。
具体解释如下:
- Unicode:统一码,是一个全球字符编码标准,为世界上几乎所有的文字、符号分配唯一的编号(称为“码点”,Code Point)。
- Transformation Format:转换格式,指将 Unicode 码点转换为计算机可存储和传输的字节序列的规则。
- 8-bit:表示这种格式使用 8 位(即 1 字节)作为基本单位进行编码,并且兼容传统的 8 位字节系统。
因此,UTF-8 就是“用于表示 Unicode 字符的、以 8 位为单位的可变长度编码格式”。
🔹 为什么需要 UTF-8?
Unicode 为世界上几乎每个字符分配了一个唯一的编号(称为 码点,Code Point),例如:
A→ U+0041é→ U+00E9中→ U+4E2D🙂→ U+1F642
但这些码点只是“抽象数字”,计算机存储时需要把它们转换成字节序列。UTF-8 就是这种转换规则之一。
🔹 UTF-8 的核心特点
| 特性 | 说明 |
|---|---|
| 兼容 ASCII | 所有 ASCII 字符(U+0000 到 U+007F)在 UTF-8 中用 1 个字节 表示,且编码与 ASCII 完全相同。 |
| 可变长度 | 使用 1 到 4 个字节表示一个字符,根据码点大小自动调整。 |
| 无字节序问题 | 不像 UTF-16/32,UTF-8 没有大端/小端(endianness)问题,适合网络传输。 |
| 自同步 | 从任意字节开始,都能判断当前是否处于字符边界,便于错误恢复。 |
🔹 UTF-8 编码规则
| Unicode 码点范围(十六进制) | 字节数 | 编码模板(二进制) |
|---|---|---|
| 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 |
注意:UTF-8 不支持超过 U+10FFFF 的码点(这是 Unicode 的上限)。
🔹 实例解析
1. 字符 é(U+00E9)
- 码点:0x00E9 =
11101001(二进制) - 落在 U+0080–U+07FF 区间 → 用 2 字节
- 套用模板:
- 高 5 位:
00011 - 低 6 位:
101001
- 高 5 位:
- 编码:
- 第一字节:
110+00011=11000011= 0xC3 - 第二字节:
10+101001=10101001= 0xA9
- 第一字节:
- UTF-8 字节序列:C3 A9
在八进制中:
- 0xC3 = 195 →
\303 - 0xA9 = 169 →
\251
所以 C 字符串中显示为:\303\251
2. 字符 ’(U+2019,右单引号)
- 码点:0x2019
- 落在 U+0800–U+FFFF → 3 字节
- 二进制:
0010 0000 0001 1001 - 分成三组(4-6-6):
00100,000001,1001→ 补零为00100,000001,001001 - 套模板:
1110+00100→11100010= 0xE210+000001→10000001= 0x81?等等,重新对齐!
更准确的做法:
0x2019 = 0010 0000 0001 1001
拆成 4-6-6(从高位):
- 高 4 位:
0010 - 中 6 位:
000000 - 低 6 位:
011001
→ 实际应为:
1110+0010→11100010= 0xE210+000000→10000000= 0x8010+011001→10011001= 0x99
✅ UTF-8:E2 80 99 → 八进制:\342\200\231
🔹 在 C 语言中的表现
C 语言的 char 字符串本质上是字节数组。如果你在源码中写:
char *s = "géant";
而你的源文件保存为 UTF-8,那么编译器会把 é 存为两个字节 0xC3 0xA9。
当某些工具(如调试器、日志函数、旧版编译器)输出字符串时,如果遇到非 ASCII 字节(≥128),它们会以 八进制转义序列 显示,比如 \303\251,而不是直接显示 é。
但这不影响程序正确性——只要终端/输出设备使用 UTF-8 解码,就能正确显示。
🔹 如何验证?
在 Linux/macOS 终端运行:
echo "Voyez le brick géant" | hexdump -C
你会看到类似:
... 67 c3 a9 61 6e 74 ...
↑↑
'é' 的 UTF-8 编码
✅ 总结
- UTF-8 是 Unicode 的一种高效、兼容 ASCII 的编码方式。
- 非 ASCII 字符会被编码为 2~4 个字节。
- C 语言中看到的
\303\251是 UTF-8 字节的八进制转义表示,不是错误。 - 只要系统环境支持 UTF-8(现代系统基本都支持),字符串就能正常显示。
如需在 C 中明确使用 UTF-8 字符串(C11 起):
const char *msg = u8"près du wharf"; // u8 前缀确保 UTF-8 编码
这能提高代码的可移植性和可读性。
浙公网安备 33010602011771号