每日壁纸

Unicode 与字符编码:一篇讲明白

Published on
/6 mins read/---

Unicode 与字符编码:一篇讲明白

很多 Python 程序员都遇到过 UnicodeDecodeError,也常被问到「UTF-8 到底是什么」。这篇文章用最少的概念把这件事讲清楚:Unicode 是字符集,编码是存储方式,两者不是一回事。

一、Unicode:全世界字符的字典

Unicode 给世界上每一个字符分配了一个唯一的编号,叫码点(code point)。

码点范围:U+0000 ~ U+10FFFF(共 1,114,112 个位置)
字符码点十进制
AU+004165
中U+4E2D20013
文U+658725991

Python 里可以直接互相转换:

ord("中")       # 20013        字符 → 码点
hex(20013)      # '0x4e2d'    码点 → 十六进制
"\u4e2d"        # '中'         码点 → 字符
"\U00004e2d"    # '中'         同上,大写 U 表示 8 位十六进制

二、ASCII 与 Unicode 的关系

ASCII 是最早的字符编码标准,只有 0~127 共 128 个字符,覆盖英文、数字和常用符号。

Unicode 在设计上完全兼容 ASCII:码点 U+0000~U+007F 就是 ASCII 那 128 个字符。

ASCIIUnicode
范围0~127(128 个)0~10FFFF(110 万+)
能表示英文、数字、符号全世界所有文字
存储占用固定 1 字节1~4 字节(取决于编码方式)

一句话结论:ASCII 是 Unicode 的子集,Unicode 是 ASCII 的超集。

三、编码:把码点变成字节

码点只是「编号」,计算机最终要存的是字节。把这个编号翻译成字节的过程,叫编码(encoding)。

以「中」= U+4E2D = 20013 为例,同一个字符在不同编码下占用空间完全不同:

UTF-8   →  3 字节:e4 b8 ad
UTF-16  →  2 字节:4e 2d
UTF-32  →  4 字节:00 00 4e 2d
GBK     →  2 字节:中

为什么会有这么多编码?

因为没有一种方案在所有场景都最优:

编码优势典型场景
ASCII固定 1 字节,最省空间纯英文环境
UTF-8英文只占 1 字节,不浪费空间,且完全兼容 ASCIIWeb、网络、Linux(事实标准)
UTF-16中日韩文字统一 2 字节Windows、Java
UTF-32固定 4 字节,程序处理最简单程序内部计算
GBK中文老系统的兼容方案老旧中文系统

UTF-8 的变长规则

UTF-8 是变长编码,字节数由码点大小决定:

码点范围字节数示例
U+0000 ~ U+007F1A → 0x41
U+0080 ~ U+07FF2é → 0xc3 0xa9
U+0800 ~ U+FFFF3中 → e4 b8 ad
U+10000 以上4😄 → f0 9f 98 84

这个设计的精妙之处:纯英文文本在 UTF-8 下和 ASCII 完全一样,一个字节都不浪费。

四、Python3 里的字符串

Python 3 做了一个重要决定:str 永远是 Unicode 字符串,内部就存码点,不存在「编码歧义」。

s = "中文"                    # str,Unicode 字符串
b = s.encode("utf-8")        # str → bytes,编码
s = b.decode("utf-8")        # bytes → str,解码

编码和解码是一对逆操作:

s = "中文"
b = s.encode("utf-8")        # b'\xe4\xb8\xad\xe6\x96\x87'
b.decode("utf-8")            # '中文'

UnicodeDecodeError 的本质:用 A 编码写进去,用 B 编码读出来,翻译对不上。

五、文件读写:最容易踩坑的地方

str 存进文件时必须指定编码。Python 3 不会帮你猜,也不会报错——它会默默使用平台默认值,在你的电脑上正常,换一台机器就炸。

# ✅ 推荐:显式指定编码
with open("file.txt", "w", encoding="utf-8") as f:
    f.write("中文")
 
with open("file.txt", encoding="utf-8") as f:
    content = f.read()
 
# ❌ 不推荐:依赖系统默认
open("file.txt", "w").write("中文")

补充:很多老代码里会写 sys.setdefaultencoding("utf-8"),这是无效的——Python 3 里这个函数被冻结了,改不了任何行为。真正要改的是 open() 的 encoding 参数,以及文件本身声明的编码。

六、记住这一句话就够了

Unicode 是字符集(有哪些字符),编码是存储方式(怎么存字节)。

Python3 里 str 用 Unicode,bytes 用编码;两者之间的转换只有 encode() 和 decode()。

速查表

ord("中") / chr(20013)              # 码点 ↔ 字符
"\u4e2d"                            # 转义写法
s.encode("utf-8") / b.decode("utf-8")  # str ↔ bytes
open(f, encoding="utf-8")            # 读文件