Python 2 的 str 是字节序列而 Python 3 的 str 是 Unicode 文本,混用 bytes 和 str 会导致 UnicodeDecodeError 或 UnicodeEncodeError;读写文件必须显式指定 encoding,网络响应需用 .text 或正确 decode,避免 double-decode 和隐式编码。

Python 2 和 Python 3 的 str 类型语义完全不同
这是绝大多数编码问题的起点。Python 2 中 str 是字节序列,unicode 才是文本;而 Python 3 中 str 是 Unicode 文本,bytes 才是字节序列。一旦混用(比如把 bytes 当 str 传给 print 或写入文件),就可能触发 UnicodeDecodeError 或 UnicodeEncodeError。
常见错误现象:UnicodeDecodeError: 'utf-8' codec can't decode byte 0xe4 in position 0: invalid continuation byte,本质是拿 UTF-8 编码的 bytes 强行当 Unicode 字符串去 decode(比如重复调用 .decode('utf-8'))。
- 读文件时显式指定
encoding参数,不要依赖默认(Python 3 默认是utf-8,但系统 locale 可能干扰) - 网络响应(如
requests.get().content)返回的是bytes,需用.text(自动解码)或手动.content.decode('utf-8'),别直接str(response.content) - 避免对已解码的
str再调用.decode()—— 这是典型“double-decode”错误
open() 不指定 encoding 就等于埋雷
Python 3 的 open() 在文本模式下必须知道如何把字节转成字符,否则依赖系统 locale(Windows 常为 cp936,Linux/macOS 多为 UTF-8),导致同一段代码在不同机器上行为不一致。
示例:在 Windows 上用 open('data.txt').read() 读取 UTF-8 编码的文件,大概率报错;而在 macOS 上可能正常——这不是代码“对”,只是碰巧。
立即学习“Python免费学习笔记(深入)”;
- 始终显式写
open('file.txt', encoding='utf-8'),除非你明确需要其他编码(如处理旧版 GBK 日志) - 写文件时也加
encoding,避免用str.encode()后再写入文本模式文件(会触发隐式二次编码) - 用
locale.getpreferredencoding()查当前默认编码,仅作调试用,别用于生产逻辑
终端/IDE 的编码设置与 Python 解码逻辑不匹配
即使 Python 正确解码了字符串,print() 仍可能失败——因为终端或 IDE 的字符集不支持该 Unicode 字符,或其 stdout 的 encoding 被设为不兼容的值(如 None 或 cp1252)。
常见错误现象:UnicodeEncodeError: 'charmap' codec can't encode character '\u2019' in position 123,多出现在 Windows CMD 或某些老旧 IDE 中。
- 检查
sys.stdout.encoding,不是所有环境都等于utf-8 - 临时绕过:用
print(s.encode('utf-8', errors='replace').decode('utf-8'))不现实;更稳妥的是捕获异常后 fallback 到repr(s) - PyCharm / VS Code 默认支持 UTF-8,但需确认终端模拟器(如 Windows Terminal)字体支持 Unicode
第三方库返回的字符串类型容易被忽略
很多库(如 json、csv、sqlite3)在 Python 3 中默认返回 str,但有些(如早期 requests 或某些 C 扩展)可能返回 bytes,尤其在未配置参数时。
示例:json.loads(b'{"k": "v"}') 在 Python 3.6+ 返回 dict,但键值仍是 str;而 json.load(fp) 若 fp 是以二进制打开的文件,则必须先 decode,否则报错。
- 用
isinstance(x, str)和isinstance(x, bytes)显式判断,别靠经验猜 - 处理 CSV 时,
csv.reader(f)要求f是文本流,若传入open(..., 'rb'),会直接报错TypeError: a bytes-like object is required - SQLite 的
text_factory默认为str,但可设为bytes或自定义函数——改了就得配套处理
最常被忽略的一点:编码问题往往不是孤立发生的,而是多个环节的编码假设层层叠加后崩塌。比如文件用 GBK 存、open() 按 UTF-8 读、再传给一个默认按系统编码打印的终端——三处错,但错误信息只在最后一环抛出。










