Python 编码问题:UnicodeEncodeError 与字符编码
Python 3 处理文本的方式与硬盘存储或网络传输数据的方式存在根本差异。这种差异导致了 UnicodeEncodeError 和 UnicodeDecodeError 的频繁出现。理解并解决这些问题的核心在于明确区分“人类可读的字符串”与“机器存储的字节流”之间的转换关系。
1. 核心概念:字符集与编码
计算机并不直接认识“中”、“a”、“!”这些字符。它只认识 0 和 1。
- 字符集:给每个字符分配一个唯一的编号(ID)。例如,“中”字的 Unicode 编号是
20013。 - 编码:规定如何将这个编号转换成二进制字节。例如,UTF-8 编码将
20013转换成三个字节\xe4\xb8\xad,而 GBK 编码将其转换成两个字节\xd6\xd0。
在 Python 内部,字符串默认使用 Unicode(字符集)。当你把字符串存入文件、打印到控制台或发送到网络时,Python 必须把 Unicode 转换成字节流(编码);反之,从文件读取或接收网络数据时,必须把字节流转换回 Unicode(解码)。
以下是数据在程序内部与外部流转的抽象过程:
2. 读取文件时的解码错误
当你尝试读取一个文本文件时,如果 Python 猜测的编码方式与文件实际保存的编码方式不一致,就会报 UnicodeDecodeError。
场景复现
运行 以下代码尝试读取一个使用 GBK 编码保存的文件,但代码默认使用 UTF-8 解码:
# 假设 file.txt 是用 GBK 编码保存的中文文件
with open('file.txt', 'r') as f:
content = f.read()
报错信息通常为:UnicodeDecodeError: 'utf-8' codec can't decode byte 0xd6 in position...
解决步骤
-
确认 文件的原始编码格式。
- 在 Windows 上,记事本默认保存为
ANSI(通常是 GBK)。 - 在 Linux 或现代编辑器中,默认通常是
UTF-8。 - 你可以使用编辑器(如 VS Code)右下角查看编码,或使用命令行工具
file -i filename(Linux/Mac)查看。
- 在 Windows 上,记事本默认保存为
-
修改
open函数,显式指定正确的encoding参数。如果文件是 GBK 编码:
# 指定 encoding='gbk' with open('file.txt', 'r', encoding='gbk') as f: content = f.read() print(content)如果文件是 UTF-8 编码:
# 指定 encoding='utf-8' with open('file.txt', 'r', encoding='utf-8') as f: content = f.read()
3. 写入文件或控制台时的编码错误
当你将 Python 内部的 Unicode 字符串写入文件、打印到控制台,或者通过 HTTP 传输时,Python 需要将其“编码”成字节。如果目标环境不支持这些字符,就会报 UnicodeEncodeError。
场景 A:Windows 控制台打印乱码
Windows 的 CMD 或 PowerShell 默认代码页通常是 GBK (cp936)。如果你直接打印包含特殊符号或某些生僻字的字符串,可能会报错。
运行 以下代码:
text = "某些特殊字符 ℃"
print(text)
报错信息:UnicodeEncodeError: 'gbk' codec can't encode character '\u2103' in position...
解决方法
-
设置 环境变量
PYTHONIOENCODING为utf-8。- 在命令行执行前先运行:
set PYTHONIOENCODING=utf-8(Windows)。 - 或者在 Python 脚本的最开头添加:
import sys import io # 修改标准输出流的编码为 UTF-8 sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8') text = "某些特殊字符 ℃" print(text) - 在命令行执行前先运行:
场景 B:写入文件时指定编码
打开 文件时,同样必须指定 encoding,否则 Python 将使用平台默认编码(Windows 是 GBK,Linux 是 UTF-8),导致代码在不同机器上行为不一致。
执行 以下操作确保跨平台兼容性:
data = "这是要保存的数据"
# 始终显式指定 encoding='utf-8'
with open('output.txt', 'w', encoding='utf-8') as f:
f.write(data)
注意:如果你希望 Excel 能直接双击打开不乱码,可以使用 utf-8-sig(它会添加 BOM 头):
with open('output.csv', 'w', encoding='utf-8-sig') as f:
f.write("姓名,年龄\n张三,18")
4. 网络请求与爬虫中的编码处理
在使用 requests 或 urllib 抓取网页时,服务器返回的通常是字节流(bytes)。如果你直接对其使用正则表达式或字符串方法,会报错。
步骤指南
-
检查 响应对象的编码。
- 使用
requests库时,它会自动从 HTTP Header 中猜测编码。
import requests response = requests.get('https://www.example.com') # 查看 requests 猜测的编码 print(response.encoding) - 使用
-
修正 编码(如果自动猜测错误)。
- 如果网页内容乱码,手动设置
response.encoding为正确的编码(通常是utf-8或gbk)。
response = requests.get('https://www.example.com') # 如果发现乱码,强制设置为 utf-8 if response.encoding != 'utf-8': response.encoding = 'utf-8' # 获取解码后的字符串 html_text = response.text - 如果网页内容乱码,手动设置
-
处理 纯字节响应。
- 如果响应头没有编码信息,你需要对
response.content(字节)手动解码。
# 获取原始字节 raw_bytes = response.content # 尝试用 utf-8 解码 try: text = raw_bytes.decode('utf-8') except UnicodeDecodeError: # 如果失败,回退到 gbk text = raw_bytes.decode('gbk') - 如果响应头没有编码信息,你需要对
5. 编码问题速查表
下表总结了常见操作、默认行为及推荐写法。
| 操作场景 | 常见错误 | 推荐参数/代码 |
|---|---|---|
| 读取文本文件 | UnicodeDecodeError |
open('f.txt', 'r', encoding='utf-8') |
| 写入文本文件 | 写入后乱码 / 换机器报错 | open('f.txt', 'w', encoding='utf-8') |
| 写入 CSV (Excel) | Excel 打开乱码 | open('f.csv', 'w', encoding='utf-8-sig') |
| Windows 打印 | UnicodeEncodeError |
sys.stdout.reconfigure(encoding='utf-8') |
| 字符转字节 | TypeError |
my_str.encode('utf-8') |
| 字节转字符 | UnicodeDecodeError |
my_bytes.decode('utf-8') |
| 忽略错误字符 | 因个别坏字符导致程序崩溃 | decode('utf-8', errors='ignore') |
6. 紧急规避策略
如果只是为了让程序跑通,而不在乎少量字符丢失,可以使用错误忽略策略。但这仅适用于非关键数据的日志处理或清洗。
-
解码时忽略错误(适用于读取脏数据):
# 即使遇到无法解码的字节也跳过,不抛出异常 clean_text = dirty_bytes.decode('utf-8', errors='ignore') -
编码时替换错误(适用于输出到不支持全字符的终端):
# 将无法编码的字符替换为问号 ? safe_str = dangerous_str.encode('gbk', errors='replace').decode('gbk')
定位 报错发生的位置(是读取、写入还是网络传输),判断 是编码还是解码阶段,然后选择上述对应的方法修复即可。

暂无评论,快来抢沙发吧!