解决Python读取txt文件时的编码报错问题

林舟
林舟
发布时间:2026-11-03| 7人浏览过
原创

针对Python使用open()读取txt文件时常见的UnicodeDecodeError报错,详细解析操作系统默认编码机制、UTF-8与GBK/GB18030的冲突根因,并提供显式编码声明、BOM头过滤、自动编码探测及errors容错参数的完整实操指南。

一、典型报错现象与具体表现

假设我们编写了一段简短的代码来读取本地生成的 data.txt 文本:

# 常见但潜藏隐患的简写方式
with open("data.txt", "r") as f:
    content = f.read()
print(content)

在很多情况下,终端会立即中断并输出以下两种最常见的报错信息之一:

# 报错类型 A(在 Windows 环境或指定 UTF-8 解码 GBK 文件时常见)
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xc4 in position 0: invalid start byte

# 报错类型 B(在默认 GBK 的 Windows 控制台下读取 UTF-8 文件时常见)
UnicodeDecodeError: 'gbk' codec can't decode byte 0x80 in position 14: illegal multibyte sequence

仔细观察报错信息会发现,UnicodeDecodeError 通常包含四个固定字段:解码器名称(如 'utf-8' 或 'gbk')、遇到问题的具体物理字节(如 byte 0xc4)、出错的字节索引位置(position),以及具体的协议违规原因(如“不是合法的起始字节”或“非法的多字节序列”)。

二、编码报错发生的底层机理

计算机磁盘在物理上只存储由 0 和 1 构成的二进制字节(Bytes)。人类能够阅读的字符(如中文字符“你”、“好”)必须通过某种字符集编码标准(如 UTF-8、GBK)转换为字节序列写入磁盘。当 Python 打开文本文件时,它必须逆向执行这一过程,即将原始字节流逐一翻译回内存中的 Unicode 字符串。

问题在于:纯文本文件(.txt)在文件头通常并不自带格式元数据来标明自己使用的是什么编码。如果在调用 open() 时省略了 encoding 参数,Python 将只能调用操作系统的默认偏好设置(在 Windows 中由 locale.getpreferredencoding() 返回,通常为 cp936 即 GBK;在 Linux 和 macOS 中通常为 utf-8)。

解决Python读取txt文件时的编码报错问题
图 1:UnicodeDecodeError 发生的核心机理与底层映射失败分析

如图 1 所示,中文字符“你好”在 GBK 编码下占用 4 个字节(0xC4 0xE3 0xBA 0xC3),而在 UTF-8 编码下占用 6 个字节(0xE4 0xBD 0xA0 0xE5 0xA5 0xBD)。当 Python 的 UTF-8 解码器读到 GBK 的第一个字节 0xC4 时,根据 UTF-8 标准,0xC4 意味着后续必须紧跟符合 10xxxxxx 规范的连续字节,而实际紧随其后的 0xE3 打破了这一规则,解码器判定数据损坏,立即抛出 UnicodeDecodeError。

三、系统化排查与解决方案决策树

解决此类报错并不需要盲目尝试各种偏门参数。按照合理的诊断顺序,可以在几秒内锁定根本原因并恢复正常读取。

解决Python读取txt文件时的编码报错问题
图 2:Python 读取 txt 文本时的编码排查决策路径

步骤 1:显式指定 encoding='utf-8'

目前绝大多数由现代编辑器(VS Code、PyCharm、Sublime Text)、网络请求、开源数据集或 Linux/Mac 系统生成的文本文件,默认均采用 UTF-8 编码。

因此,排查的第一步是检查你的代码中是否遗漏了 encoding 参数。补齐该参数即可解决跨平台迁移时出现的绝大多数报错:

# 显式指定 UTF-8 编码进行安全读取
with open("data.txt", "r", encoding="utf-8") as f:
    content = f.read()

步骤 2:针对 Windows 历史文本改用 encoding='gb18030'

如果显式传入 encoding='utf-8' 后,依然弹出 'utf-8' codec can't decode byte... 报错,则说明该文件大概率是在 Windows 简体中文环境下由系统自带的“记事本”或老旧软件导出的 ANSI 文本(即 GBK 系列编码)。

此时,强烈建议直接使用 encoding='gb18030',而不是仅使用 encoding='gbk'。

为什么推荐 GB18030 而非 GBK?

GB18030 是中国国家标准的强制性编码规范,它是 GBK 和 GB2312 的严格超集。GBK 仅收录了 2 万多个汉字,遇到生僻字(如人名“䶮”、“堃”、地名字)或部分少数民族文字时,GBK 仍会触发 UnicodeDecodeError,而 GB18030 完整支持全量 Unicode 字符集,能最大程度保证中文古籍与生僻字正常还原。

# 兼容所有 GBK/GB2312 中文及生僻字的稳妥写法
with open("data.txt", "r", encoding="gb18030") as f:
    content = f.read()

步骤 3:消除隐形 BOM 标记——使用 encoding='utf-8-sig'

有时候文件读取并没有报错,但后续处理却诡异失败:例如第一行的字符串无法被 int() 转换、CSV/TXT 首行表头作为字典 key 无法匹配,或者在控制台打印时第一行开头多出一个空位。

这是因为 Windows 记事本在保存 UTF-8 文件时,习惯在文件物理最前端追加 3 个字节的“字节顺序标记”(BOM,十六进制为 0xEF 0xBB 0xBF)。当使用普通的 encoding='utf-8' 读取时,Python 会将该标记解码为不可见的 Unicode 字符 。

此时只需将编码声明改为 encoding='utf-8-sig'。Python 在读取时会自动识别并剔除开头的 BOM 头,同时它完全兼容没有 BOM 的普通 UTF-8 文件:

# 自动识别并剥除 Windows BOM 标记
with open("data.txt", "r", encoding="utf-8-sig") as f:
    content = f.read()

步骤 4:批量处理来源未知的文本文件——自动嗅探与智能回退

当需要编写爬虫批量清洗海量 txt 文本,或者处理来自不同部门、不同操作系统汇总的文件集合时,人工猜测编码不再可行。可以通过预先读取少量物理二进制字节,利用统计学模型自动探测其真实编码。

推荐使用现代 Python 生态中最主流的 charset-normalizer 库(该库也是广泛使用的 requests 库底层编码引擎):

from charset_normalizer import from_bytes

def smart_read_txt(file_path: str) -> str:
    # 1. 以纯二进制方式读取前 10KB 样本,避免整文件读入内存
    with open(file_path, "rb") as f:
        sample_bytes = f.read(10240)
    
    # 2. 如果文件为空,直接返回空串
    if not sample_bytes:
        return ""
    
    # 3. 统计学预测最可能的编码格式
    detection = from_bytes(sample_bytes).best()
    detected_encoding = detection.encoding if detection else "utf-8"
    
    # 4. 尝试根据检测出的编码读取,并设置备用回退列表
    fallback_encodings = [detected_encoding, "utf-8", "gb18030", "utf-8-sig"]
    
    for enc in fallback_encodings:
        try:
            with open(file_path, "r", encoding=enc) as f:
                return f.read()
        except (UnicodeDecodeError, LookupError):
            continue
            
    # 5. 若全部严格解码失败,采用 replace 模式降级读取
    with open(file_path, "r", encoding="utf-8", errors="replace") as f:
        return f.read()

四、解码容错机制与 errors 参数深度对比

除了修改 encoding 参数之外,open() 函数还提供了名为 errors 的容错配置项。很多初学者在遇到报错时喜欢直接加上 errors='ignore',这种做法虽然让程序不再抛出异常,却隐藏着严重的工程隐患。

解决Python读取txt文件时的编码报错问题
图 3:open() 函数中 errors 参数不同模式的行为特征与风险对比
容错模式 非法字节处理方式 数据完整性 推荐应用场景
errors='strict' 立即抛出 UnicodeDecodeError 极高(不产生脏数据) 账目、身份信息、配置解析等核心业务(默认行为)
errors='replace' 替换为占位符号 �() 高(保留位置线索) 非结构化日志挖掘、海量数据概览、搜索引擎索引构建
errors='ignore' 静默跳过并彻底丢弃该字节 极差(字符缺失、拼写错位) 仅限提取部分 ASCII 英文且完全不在意中文丢失的场合
errors='surrogateescape' 转义映射到私有代理项 完美(支持无损还原) 操作系统底层管道、文件名传递、字节流无损原样写回
生产警示:切忌盲目依赖 errors='ignore'

当你在 GBK 编码的文件上使用 encoding='utf-8', errors='ignore' 时,一个原本占 2 个字节的汉字可能会因为其中 1 个字节被忽略,导致相邻的字节与下一个字发生“串位拼合”,整段正文将大面积变成错乱乱码。除非明确知道某文件仅混杂了个别不可见控制符,否则应优先找到真实编码,而不是跳过字节。

五、工程最佳实践与排查总结

在生产环境和日常脚本编写中,规避文本编码报错的关键在于建立规范的编码处理习惯。

解决Python读取txt文件时的编码报错问题
图 4:Python 文本文件安全读取规范全景卡片
  • 永远不要裸写 open(filename, 'r'):无论你当前使用的操作系统是 Linux、macOS 还是 Windows,都务必显式声明 encoding='utf-8'。这能确保代码在不同机器、容器或 CI/CD 流水线中具备一致的行为。
  • 中文 Windows 文本优先尝试 GB18030:遇到旧系统导出的数据,直接使用 gb18030 能够涵盖 gbk 和 gb2312 的所有字符,并避免生僻字崩盘。
  • 识别 BOM 头特征:如果使用 utf-8 读出的文本在首部带有空字符或 ,直接切换为 encoding='utf-8-sig'。
  • 分清文本模式与二进制模式:对于格式不明确的文件,可以先用 open(filename, 'rb') 观察前几个字节的十六进制值,然后再针对性地选择解码器。

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。