Black 去掉文件开头空行后文字乱码,保留原编码声明的位置
Black 移动原本被忽略的编码声明时会改变文件下次解码,应采用保留空行修复并核对格式化后的字符值。
文件原来按 UTF-8 正常运行,Black 格式化后退出 0,再运行却把 é 读成 é,要看编码声明是不是从第三行或更后面移到了文件头。Python 只识别前两行中的编码声明;原来不生效的注释,去掉前面的空行后可能开始控制解码。
例如一个以两行空行开头、第三行写着 # coding: latin-1 的 UTF-8 文件,Python 原来忽略这条声明。旧格式化逻辑移除头部空行,仍以原来的 UTF-8 写文件;下次 Python 却按 Latin-1 读取同样的字节,字符串内容就变了。AST 安全检查比较的是已经解码的文本,看不到下一次文件解码方式发生改变。
Black 26.10.1 包含修复:按源文件和格式化结果的声明位置判断解码方式,必要时保留最多两行空行,让原本被忽略的声明继续被忽略。它不会替项目猜应该采用什么编码。
python -m pip install "black==26.10.1"
可在独立目录创建一个明确按 UTF-8 写出的样例:
from pathlib import Path
source = '\n\n# coding: latin-1\nvalue = "é"\nassert value == chr(233)\n'
Path("encoding-check.py").write_text(source, encoding="utf-8")
python encoding-check.py
python -m black encoding-check.py
python encoding-check.py
python -m black --check encoding-check.py
第一次与格式化后的执行都应通过字符断言,随后检查也应通过。还要查看文件开头,确认声明没有被移到会改变解码的行。这里用 chr(233) 作基准,避免两个同样被错误解码的文字字面量互相比较,掩盖了问题。
真实项目如果不需要那条过期声明,也可以在确认文件实际编码后明确修正或移除,再验收文本值和导入行为。已有乱码产物应从完整的原文件或版本控制恢复,更新格式化工具不能自动推断并还原所有被误解码的内容。
这是声明位置改变语义的路径,和对非 UTF-8 文件重复解码、Notebook 字节偏移或 CSV 编码选择不同。BOM、特殊编辑器 modeline 和 stdin 流程应按各自入口复验;仅运行 black --fast 不会解决下一次解码契约的问题。


