Unicode vs. 编码
必须明确两个关键概念:

-
Unicode:它是一个字符集,而不是一种编码,你可以把它想象成一个巨大的字典,为世界上几乎所有的每一个字符(
A,中, , )都分配了一个唯一的数字,这个数字被称为码点,字符A的码点是U+0041,字符中的码点是U+4E2D,Unicode 只负责“是什么字符”,不关心“如何存储”。 -
编码:它是一套规则,规定了如何将 Unicode 码点转换成计算机可以存储和传输的字节序列,常见的编码有:
- UTF-8:目前最流行、最通用的编码,它是一种可变长编码,英文字符(如
A)占用 1 个字节,而中文字符通常占用 3 个字节,UTF-8 是 ASCII 的超集,兼容 ASCII。 - UTF-16:可变长编码,英文字符通常占用 2 个字节,中文字符通常也占用 2 或 4 个字节。
- GBK / GB2312:主要用于简体中文的编码,不是 Unicode 标准。
- UTF-8:目前最流行、最通用的编码,它是一种可变长编码,英文字符(如
一句话总结:Unicode 是“字符身份证号”,编码是“如何把身份证号存在计算机里”。
Python 2 vs. Python 3 的关键区别
理解 Python 2 和 Python 3 在处理 Unicode 上的巨大差异至关重要。

Python 3 的处理方式(推荐,现代标准)
在 Python 3 中,字符串的默认类型就是 Unicode 字符串,你写的字符串字面量,Python 会自动将其处理为 Unicode。
str 类型就是 Unicode 字符串。
str: 表示 Unicode 字符串(内存中的抽象形式)。bytes: 表示字节序列(磁盘上或网络中传输的实际数据)。
转换的核心是 encode() 和 decode() 方法:
str.encode(encoding): 将 Unicode 字符串编码成字节序列。bytes.decode(encoding): 将字节序列解码成 Unicode 字符串。
Python 3 中的具体操作和示例
从 Unicode 字符串得到字节序列 (编码)
当你需要将字符串写入文件、发送到网络或进行其他底层操作时,需要将其编码为字节。

# 创建一个 Unicode 字符串
# 在 Python 3 中,'你好' 默认就是 str (Unicode) 类型
s_unicode = "你好,世界!"
print(f"原始字符串类型: {type(s_unicode)}")
print(f"原始字符串内容: {s_unicode}")
# --- 编码成不同的格式 ---
# 1. 编码成 UTF-8 字节序列
# 这是推荐的做法
s_utf8_bytes = s_unicode.encode('utf-8')
print(f"\n编码为 UTF-8 后的类型: {type(s_utf8_bytes)}")
print(f"编码为 UTF-8 后的内容: {s_utf8_bytes}")
# 输出: b'\xe4\xbd\xa0\xe5\xa5\xbd\xef\xbc\x8c\xe4\xb8\x96\xe7\x95\x8c\xef\xbc\x81'
# 每个字节都以 \x 开头
# 2. 编码成 UTF-16 字节序列
s_utf16_bytes = s_unicode.encode('utf-16')
print(f"\n编码为 UTF-16 后的类型: {type(s_utf16_bytes)}")
print(f"编码为 UTF-16 后的内容: {s_utf16_bytes}")
# 输出: b'\xff\xfe=\xe4\x8b\xbd\xbb\xbf,\x20\x16\xc7\x8d\x81\xef'
# 注意开头有 BOM (Byte Order Mark) \xff\xfe
# 3. 编码为 GBK (针对中文的特定编码)
s_gbk_bytes = s_unicode.encode('gbk')
print(f"\n编码为 GBK 后的类型: {type(s_gbk_bytes)}")
print(f"编码为 GBK 后的内容: {s_gbk_bytes}")
# 输出: b'\xc4\xe3\xba\xc3\xa1\xa3\xac\xca\xa1\xca\xa0\xxb9\xe3\x81'
从字节序列得到 Unicode 字符串 (解码)
当你从文件或网络读取数据时,得到的是字节序列,需要将其解码成 Unicode 字符串才能进行文本处理。
# 假设我们有一个从文件或网络读取的 UTF-8 编码的字节序列
bytes_from_file = b'\xe4\xbd\xa0\xe5\xa5\xbd' # 这是 "你好" 的 UTF-8 编码
# --- 解码成 Unicode 字符串 ---
# 1. 使用 UTF-8 解码
# 这必须和编码时使用的格式一致!
s_unicode_decoded = bytes_from_file.decode('utf-8')
print(f"解码后的类型: {type(s_unicode_decoded)}")
print(f"解码后的内容: {s_unicode_decoded}")
# 输出: 你好
# 2. 错误示范:用错误的编码解码
# 如果编码和解码的格式不一致,会抛出异常
try:
s_unicode_decoded_wrong = bytes_from_file.decode('gbk')
except UnicodeDecodeError as e:
print(f"\n使用错误的编码解码时出错: {e}")
# 输出: 使用错误的编码解码时出错: 'gbk' codec can't decode byte 0xe4 in position 0: illegal multibyte sequence
处理 Python 2 的遗留数据
有时你可能会遇到 Python 2 生成的文件或数据,str 类型是字节串,而 unicode 类型才是真正的 Unicode 字符串。
str(Python 2): 字节串,等同于 Python 3 的bytes。unicode(Python 2): Unicode 字符串,等同于 Python 3 的str。
在 Python 3 中处理这类数据,你需要先解码它:
# 假设这是从 Python 2 程序或一个旧文件中读取的 'str' (字节串)
py2_str = "你好"
# 在 Python 3 中,它的类型是 bytes
print(f"Python 2 风格的 'str' 在 Python 3 中的类型: {type(py2_str)}")
# 输出: <class 'bytes'>
# 你需要先知道它当初是用什么编码的,然后进行解码
# Python 2 的代码文件默认是 ASCII,但处理中文时常用 UTF-8
py2_unicode_str = py2_str.decode('utf-8') # 或者 'latin-1' 等等
print(f"解码后的类型: {type(py2_unicode_str)}")
print(f"解码后的内容: {py2_unicode_str}")
# 输出: 你好
最佳实践和总结
-
永远使用 Python 3,它在处理 Unicode 方面设计得更清晰、更安全。
-
在程序内部,始终使用
str(Unicode) 类型 进行所有文本操作(字符串拼接、分割、查找等),不要在内存中混用str和bytes。 -
只在需要和外部世界交互时才进行编码/解码:
- 写入文件:
my_string.encode('utf-8') - 从文件读取:
file.read().decode('utf-8') - 发送网络请求:
my_string.encode('utf-8') - 接收网络响应:
response.read().decode('utf-8')
- 写入文件:
-
统一使用 UTF-8 作为你的标准编码,除非你有特殊的历史遗留系统或硬件限制,否则 UTF-8 是最佳选择。
-
处理编码错误:如果无法保证数据编码的 100% 准确性,可以在
encode或decode时指定错误处理方式,而不是直接崩溃。errors='ignore': 忽略无法编码/解码的字符。errors='replace': 用占位符(如 )替换无法编码/解码的字符。errors='strict': 遇到错误时抛出异常(默认行为)。
# 示例:替换无法解码的字节 bad_bytes = b'\xe4\xa0\xa0\xe4\xa0' # '好' 的 UTF-8 编码,但最后一个字节不完整 s = bad_bytes.decode('utf-8', errors='replace') print(s) # 输出: 好�
希望这个详细的解释能帮助你彻底理解 Python 中的 Unicode 问题!
