返回博客

故障排查指南

在 Python 中打印 LLM 回复时出现 UnicodeEncodeError:3 个修复

print(reply) 在西里尔字母或 emoji 上崩溃?了解为什么 Python 在重定向输出或旧版代码页下失败,并用 UTF-8 模式、reconfigure 或 encoding= 修复它。

作者:CheapTokens Team发布于 5 分钟阅读

print(reply) 上的 UnicodeEncodeError 和 UTF-8 修复,绘制为错误卡片
本页内容
  1. 错误长什么样
  2. 检查你实际使用的编码
  3. 修复 1:开启 UTF-8 模式
  4. 修复 2:在脚本内重新配置 stdout
  5. 修复 3:为文件指定显式编码
  6. 你应该用哪个修复?
  7. 如果仍然失败

你的请求已经发出,模型也给出了回答,然后 Python 在 print(reply) 上崩溃了。问题不在 API。回复本身没问题;是你打印它的地方无法表示其中的某些字符。最快的修复方法是用 PYTHONUTF8=1 开启 UTF-8 模式。如果你更希望把它留在脚本里,就在顶部调用 sys.stdout.reconfigure(encoding="utf-8"),而当你把回复保存到文件时,给 open() 传入 encoding="utf-8"。

错误长什么样#

假设你用俄语向模型提问并打印答案。这就是完整的脚本,使用我们首次请求指南中的普通 requests 调用:

python
import os, requests

r = requests.post(
    "https://api.cheaptokens.dev/v1/chat/completions",
    headers={"Authorization": f"Bearer {os.environ['CHEAPTOKENS_API_KEY']}"},
    json={
        "model": "deepseek-ai/DeepSeek-V4-Flash-0731",
        "messages": [{"role": "user", "content": "Ответь одним предложением: что такое токен?"}],
        "max_tokens": 200,
    },
)
reply = r.json()["choices"][0]["message"]["content"]
print(reply)

当 stdout 被重定向到文件或管道,或者 Python 启动时为其流使用了旧版代码页,最后一行会像这样结束:

text
UnicodeEncodeError: 'charmap' codec can't encode characters in position 0-5: character maps to <undefined>

你消息中的编解码器名称取决于流的编码(对于 Windows 代码页通常是 charmap,对于 ascii 则是 ascii),而位置取决于回复内容。形态始终相同:回溯指向 print,而不是请求。

一个指向 print(reply) 的 UnicodeEncodeError 回溯
错误的典型形态:它由 print 抛出,此时 API 调用已经成功

为什么会发生?print 使用 sys.stdout 的编码将文本转换为字节。如果该编码是旧版代码页,那么任何不在其中的字符——例如西文代码页中的西里尔字母、emoji,有时还有排版破折号——都没有可转换成的字节。Python 会抛出异常,而不是猜测。

它出现在三种情况下:

  • stdout 被重定向或管道传输(python app.py > out.txt、| another program),或者脚本从调度器、CI 或编辑器中运行,而那里的 stdout 不是控制台;
  • 设置了 PYTHONLEGACYWINDOWSSTDIO,或者流以其他方式获得了窄编码;
  • 你用 open(path, "w") 把回复写入文件,且没有 encoding=。

哪个 Python? 以下内容都针对 Python 3.7–3.14,其中流和文件编码来自系统。根据 PEP 686(状态为 Final,https://peps.python.org/pep-0686/, 于 2026 年 11 月 5 日检查),从 Python 3.15 起 UTF-8 模式默认开启,因此在那里 open() 和 stdio 已经使用 UTF-8,除非有人用 PYTHONUTF8=0 或 -X utf8=0 将其关闭。用 python --version 检查。

检查你实际使用的编码#

在更改任何内容之前,先看看 Python 选了什么。只需十秒钟。

自己试试试一试:Python 认为你的 stdout 编码是什么?显示答案

在脚本失败的那个窗口中运行 python -c "import sys; print(sys.stdout.encoding)"。在交互式控制台中,它通常打印 utf-8,所以仅凭这一点说明不了什么。在末尾加上 > check.txt 再运行一次并打开文件:重定向输出更常给出 cp… 名称或 ascii,那就是罪魁祸首。如果一切都显示 utf-8,就看看你写入的文件。

想不等倒霉日子就看看崩溃?在 macOS 或 Linux 上,为一次运行强制使用窄编码:

bash
PYTHONIOENCODING=ascii python -c "print('Привет')"

这会失败并给出 ascii 编解码器消息,与上面的错误属于同一类。在 Windows 上,该变量对交互式控制台会被忽略,但在输出被重定向时有效:在 cmd 中运行 set PYTHONIOENCODING=ascii,然后运行 python -c "print('Привет')" > out.txt(在 PowerShell 中:$env:PYTHONIOENCODING = "ascii")。

修复 1:开启 UTF-8 模式#

Python 有一个模式,使 UTF-8 成为文本的默认编码。根据 Python 文档(命令行页面,于 2026 年 11 月 5 日检查),PYTHONUTF8=1 会开启它,-X utf8 效果相同;两者自 Python 3.7 起就存在。同一页面还有一个注意事项:PYTHONUTF8 的任何其他非空值都会导致启动时出错,所以请准确使用 1(或用 0 将其关闭)。在 3.15 上,你只需要这个来在较旧解释器上显式启用该模式;如果那里已经开启,就在你的环境中查找 PYTHONUTF8=0。

为一次运行开启 UTF-8 模式

cmd

bat
set PYTHONUTF8=1
python app.py

PowerShell

powershell
$env:PYTHONUTF8 = "1"
python app.py

macOS / Linux

bash
PYTHONUTF8=1 python app.py

无需更改代码,而且它覆盖该次运行中的控制台、重定向和文件。如果你不想每次都设置变量,python -X utf8 app.py 从命令行给出相同结果。

还有 PYTHONIOENCODING=utf-8,它只覆盖 stdin、stdout 和 stderr 的编码。在 Windows 上依赖它之前,请阅读其文档条目:自 Python 3.6 起,除非设置了 PYTHONLEGACYWINDOWSSTDIO,否则它对交互式控制台缓冲区会被忽略。重定向的文件和管道不受该例外影响。因此它可以修复 python app.py > out.txt,但在控制台窗口中仍然无效。对于 stderr,错误处理器始终是 backslashreplace。

修复 2:在脚本内重新配置 stdout#

如果你无法控制人们如何启动脚本,就在最顶部更改流本身:

python
import sys

sys.stdout.reconfigure(encoding="utf-8")
sys.stderr.reconfigure(encoding="utf-8")

print(reply)

reconfigure 是为 io.TextIOWrapper 记录的(在 3.7 中添加)。当你传入 encoding 并省略 errors 时,处理器是 strict,因此不合适的字符仍会抛出异常;对于 UTF-8 的常见情况,不会剩下任何字符。文档中有两个细节:一旦从流中读取了数据,编码就不能更改(写入后可以),并且 encoding="locale" 自 3.11 起受支持。

需要脚本永不崩溃,即使终端确实无法显示文本?添加 errors="replace"。未知字符随后会显示为 ?,而不是停止运行。这掩盖了问题而不是修复它,所以请保留给日志使用。

修复 3:为文件指定显式编码#

保存回复是同样的失败,只是在代码中更安静:

python
# before: on Python up to 3.14 this uses the system's default encoding
with open("reply.txt", "w") as f:
    f.write(reply)

# after: the file is UTF-8 on every machine
with open("reply.txt", "w", encoding="utf-8") as f:
    f.write(reply)

当你读回文件时使用相同的 encoding="utf-8",否则文本可能在读入时出错。

之前:print(reply) 抛出 UnicodeEncodeError。之后:同一行打印完整回复
同一行 print,在 UTF-8 模式之前和之后

你应该用哪个修复?#

选择你的情况

选择你的情况

我在终端中运行自己的脚本

在你的环境中设置一次 PYTHONUTF8=1,或用 python -X utf8 app.py 启动。代码中无需更改。

我把脚本交付给其他人

把 sys.stdout.reconfigure(encoding="utf-8") 放在顶部,这样无论他们如何启动都能工作。

崩溃发生在文件上,而不是屏幕上

给 open() 添加 encoding="utf-8",用于写入和读取。

你可以组合使用它们。一个既打印又保存回复的脚本需要修复 2 或修复 1 用于屏幕,以及修复 3 用于文件。

如果仍然失败#

  • 错误移到了不同的行。 那么失败的调用是另一个 print 或 write;修复必须在其运行之前应用,所以把 reconfigure 放在最顶部。
  • 错误提到 PYTHONUTF8。 该变量的值不是 1 或 0。删除它并重新设置。
  • 没有崩溃,但你在控制台中看到方块或 ?。 Python 写入了有效的 UTF-8;控制台字体或代码页无法显示它。重定向到文件并在编辑器中打开文件,以确认回复完好无损。
  • 错误是 JSONDecodeError,而不是 UnicodeEncodeError。 不同的问题:参见如何从模型回复中提取 JSON。

上面的请求代码是普通的 requests 调用;CheapTokens 文档展示了 OpenAI SDK 的相同调用。另一个模型也适用同样的修复:将 model 字段换成 zai-org/GLM-5.3-Flash(每个答案最多 8,192 个输出 token,390K 上下文)。编码错误与模型无关,只与文本的去向有关。