errors 参数决定的处理方式
每条 encode/decode 都可以带 errors 参数(也可用 register_error 自定义)。默认 strict 遇到非法序列立即抛异常,是最安全的调试与校验选择;replace 把问题字符替换为问号或替换符,适合只用于显示的场景;ignore 直接丢弃,最容易造成静默丢数据;backslashreplace 输出转义序列,便于日志排查;surrogateescape 是 PEP 383 引入的策略,把无法解码的字节映射到专用区码位,从而在 str 中保留原始字节信息,操作系统路径处理默认就用它。
基本信息
| strict(默认) | 遇到非法序列抛 UnicodeError |
|---|---|
| ignore | 丢弃无法处理的字节或字符(有损且静默) |
| replace | 替换为 ? 或 U+FFFD(有损,但有痕迹) |
| backslashreplace | 输出 \xNN 形式的转义,便于排查 |
| surrogateescape | 把问题字节映射到 U+DC80-U+DCFF,可无损往返 |
| xmlcharrefreplace / namereplace | 编码时输出实体或名字形式,用于特定输出场景 |
| 适用原则 | 校验用 strict,展示用 replace,字节保真用 surrogateescape |
典型使用场景
- 数据入库前校验:strict 模式跑一遍,确认内容没有非法字节
- 日志与报表展示:允许 replace,但要统计替换次数作为质量指标
- 文件路径与命令行参数:surrogateescape 保证字节不变,避免破坏文件名
- 坏数据普查:用 backslashreplace 打印出行内非法字节的具体位置
- 旧系统对接:目标编码受限时用 replace 或音译类策略,并明确记录被替换的内容
策略对比示例
策略对比示例
# strict:非法字节直接暴露(用于校验)
python -c "print(b'\xff\xfe'.decode('utf-8'))" 2>&1 | tail -1
# replace:替换为 U+FFFD,输出可读但已丢信息
python -c "print(repr(b'a\xffb'.decode('utf-8', errors='replace')))"
# ignore:静默丢弃,长度变化是唯一线索
python -c "print(b'a\xffb'.decode('utf-8', errors='ignore'))"
# backslashreplace:保留了字节的可读形式,便于定位
python -c "print(b'a\xffb'.decode('utf-8', errors='backslashreplace'))"
# surrogateescape:无损往返,字节可原样恢复
python -c "s=b'a\xffb'.decode('utf-8','surrogateescape'); print(s.encode('utf-8','surrogateescape'))"策略特性对照
| 策略 | 是否抛异常 | 可逆性 | 适用位置 |
|---|---|---|---|
| strict | 是 | 不涉及(直接失败) | 校验、入湖前检查 |
| ignore | 否 | 不可逆(字节丢失) | 几乎不应使用 |
| replace | 否 | 不可逆(被替换) | 仅展示层,且需统计替换量 |
| backslashreplace | 否 | 文本可再解析 | 日志与排查输出 |
| surrogateescape | 否 | 可逆(字节可还原) | 文件系统路径、字节保真通道 |
| xmlcharrefreplace | 否 | 信息不丢失(换成实体) | 输出 HTML/XML 文本 |
surrogateescape 解出的字符串含有特殊码位,直接写入数据库、JSON 序列化或再按其他编码编码时会失败或报错。它的正确用法是在字节与文本的边界处成对使用(解出后只做传递,最终按同样策略编回字节),一旦流入业务层就会引发新的异常链。
宽松策略掩盖问题:replace 与 ignore 让流程「正常结束」,但内容已经改变。生产管道即使使用宽松策略,也应统计替换或丢弃的数量,把它们作为数据质量指标,否则劣化只能靠人眼发现。
调试期建议先 strict 再放宽:先用严格模式跑一遍确认问题范围与具体字节,再针对可接受的部分放宽策略。这样既避免误判「内容没问题」,也避免一直卡在异常上无法推进。