文章目录

Python 编码问题:UnicodeEncodeError 与字符编码

发布于 2026-04-08 09:20:08 · 浏览 8 次 · 评论 0 条

Python 编码问题:UnicodeEncodeError 与字符编码

Python 3 处理文本的方式与硬盘存储或网络传输数据的方式存在根本差异。这种差异导致了 UnicodeEncodeErrorUnicodeDecodeError 的频繁出现。理解并解决这些问题的核心在于明确区分“人类可读的字符串”与“机器存储的字节流”之间的转换关系。


1. 核心概念:字符集与编码

计算机并不直接认识“中”、“a”、“!”这些字符。它只认识 0 和 1。

  • 字符集:给每个字符分配一个唯一的编号(ID)。例如,“中”字的 Unicode 编号是 20013
  • 编码:规定如何将这个编号转换成二进制字节。例如,UTF-8 编码将 20013 转换成三个字节 \xe4\xb8\xad,而 GBK 编码将其转换成两个字节 \xd6\xd0

在 Python 内部,字符串默认使用 Unicode(字符集)。当你把字符串存入文件、打印到控制台或发送到网络时,Python 必须把 Unicode 转换成字节流(编码);反之,从文件读取或接收网络数据时,必须把字节流转换回 Unicode(解码)。

以下是数据在程序内部与外部流转的抽象过程:

graph LR A["内存: str (Unicode)"] -->|调用 encode| B["硬盘/网络: bytes (UTF-8/GBK)"] B -->|调用 decode| A A -- "错误: 乱码/报错" --> B B -- "错误: 乱码/报错" --> A

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...

解决步骤

  1. 确认 文件的原始编码格式。

    • 在 Windows 上,记事本默认保存为 ANSI(通常是 GBK)。
    • 在 Linux 或现代编辑器中,默认通常是 UTF-8
    • 你可以使用编辑器(如 VS Code)右下角查看编码,或使用命令行工具 file -i filename(Linux/Mac)查看。
  2. 修改 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...

解决方法

  1. 设置 环境变量 PYTHONIOENCODINGutf-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. 网络请求与爬虫中的编码处理

在使用 requestsurllib 抓取网页时,服务器返回的通常是字节流(bytes)。如果你直接对其使用正则表达式或字符串方法,会报错。

步骤指南

  1. 检查 响应对象的编码。

    • 使用 requests 库时,它会自动从 HTTP Header 中猜测编码。
    import requests
    
    response = requests.get('https://www.example.com')
    # 查看 requests 猜测的编码
    print(response.encoding) 
  2. 修正 编码(如果自动猜测错误)。

    • 如果网页内容乱码,手动设置 response.encoding 为正确的编码(通常是 utf-8gbk)。
    response = requests.get('https://www.example.com')
    
    # 如果发现乱码,强制设置为 utf-8
    if response.encoding != 'utf-8':
        response.encoding = 'utf-8'
    
    # 获取解码后的字符串
    html_text = response.text
  3. 处理 纯字节响应。

    • 如果响应头没有编码信息,你需要对 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. 紧急规避策略

如果只是为了让程序跑通,而不在乎少量字符丢失,可以使用错误忽略策略。但这仅适用于非关键数据的日志处理或清洗。

  1. 解码时忽略错误(适用于读取脏数据):

    # 即使遇到无法解码的字节也跳过,不抛出异常
    clean_text = dirty_bytes.decode('utf-8', errors='ignore')
  2. 编码时替换错误(适用于输出到不支持全字符的终端):

    # 将无法编码的字符替换为问号 ?
    safe_str = dangerous_str.encode('gbk', errors='replace').decode('gbk')

定位 报错发生的位置(是读取、写入还是网络传输),判断 是编码还是解码阶段,然后选择上述对应的方法修复即可。

评论 (0)

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

扫一扫,手机查看

扫描上方二维码,在手机上查看本文