Windows 控制台默认 GBK:定时脚本打印一个 ✔ 就能整轮崩掉

举报
yd_241537561 发表于 2026/10/13 07:35:46 2026/10/13
【摘要】 同一份脚本本地跑得好好的,进青龙就报 UnicodeEncodeError: 'gbk' codec can't encode character '\u2714'。记录三种修法,以及为什么日志「掉字」比崩溃更难查。

踩坑现场:脚本在自己的终端里跑通,挪到 Windows 版青龙的定时任务里,日志最后一行变成UnicodeEncodeError: 'gbk' codec can't encode character '\u2714' in position 298: illegal multibyte sequence。崩溃点就是任务清单里那个勾 —— 输出「✔ 已完成」时炸的,而崩溃发生在打印之后、结果写盘之前,所以这轮任务等于白跑。

一、根因是 stdout 的编码,不是文件编码

源码文件是 UTF-8,字符串在内存里也正常,问题只在写到控制台那一刻:中文 Windows 的 活动代码页是 GBK(cp936),Python 按它编码 stdout,GBK 里没有的字符直接抛错。定时任务的日志也是走 stdout 捕获的,所以面板里看到的断链日志和手工复现是同一个原因。

二、三种修法,按优先级排

# 1) 最省事,Python 3.7+
try:
    sys.stdout.reconfigure(encoding="utf-8")
except Exception:
    pass

# 2) 兜住老版本,并且容忍无法编码的字符(宁可丢字形也别丢整行)
import io
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")

# 3) 结果落盘一律显式写 UTF-8 字节,不靠默认编码
open(path, "wb").write(text.encode("utf-8"))

第 2 种里的 errors="replace" 是关键:定时任务最怕的不是少一个图标,而是整条记录没写下去。宁可日志里出现问号,也不要让一次打印把后面二十行带走。

三、掉字比崩溃更隐蔽

另一类事故是往 UTF-8 文档里追加中文时,把带 \v 转义的字符串直接写进去,落盘后正文里混进 0x0B(垂直制表符),肉眼看是「少了一行」,grep 却因为行被截断而搜不到。做法是追加前先断言、追加后再统计:

assert "\x0b" not in payload
open(path, "ab").write(payload.encode("utf-8"))
raw = open(path, "rb").read()
assert raw.count(b"\x0b") == 0            # 整份文档都不该有 VT
assert anchor in raw.decode("utf-8")       # 并且新段落真的在里面

同样的道理适用于任何「脚本写给人看的文档」:写入之后立刻用字节数、锚点字符串、非法控制字符计数三项复核,成本三行,省掉的是第二天对着残缺日志猜。

小结

跨平台脚本里不要用图形符号当唯一状态标记,勾和叉之外还要有文字(已完成/未完成);把 stdout 编码在入口处一次性处理掉;任何追加写都要带读回校验。这三条都是崩溃一次就能记住,但代价是一轮定时任务白跑。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。