PyYAML报ScannerError主因是制表符、BOM头、中文注释编码异常或缩进不统一;应使用yaml.safe_load()并确保UTF-8无BOM编码、全空格缩进、注释顶格;推荐pydantic校验字段完整性与类型。

用 PyYAML 读取 YAML 文件时为什么报 ScannerError?
常见原因是 YAML 文件含制表符(\t)、BOM 头、中文注释编码异常,或缩进不统一(空格混用)。Python 默认的 yaml.load() 已弃用且不安全,必须显式指定 Loader。
正确做法是用 yaml.safe_load(),它只解析基础类型,避免任意代码执行风险:
import yaml
<p>with open("config.yaml", "r", encoding="utf-8") as f:
data = yaml.safe_load(f) # 不要用 yaml.load(f)
- 文件务必用 UTF-8 无 BOM 编码保存;VS Code / PyCharm 默认可能带 BOM,需手动转存
- 所有缩进必须用空格,不能用 Tab —— 大多数编辑器可设“insert spaces when pressing tab”
- 注释行开头不能有空格,否则会被误判为嵌套结构
校验字段完整性:用 pydantic 定义 Schema 最省心
手写 if 判断字段是否存在既啰嗦又难维护。用 pydantic.BaseModel 声明期望结构,加载时自动校验缺失/类型错误,还能给出清晰报错位置。
示例:要求配置中必须有 database.url 和 timeout(整数):
立即学习“Python免费学习笔记(深入)”;
from pydantic import BaseModel, HttpUrl
from typing import Optional
<p>class Config(BaseModel):
database: dict
timeout: int
debug: bool = False</p><h1>加载后直接校验</h1><p>try:
config = Config(**data)
except Exception as e:
print(f"配置校验失败:{e}")
raise
-
**data传入字典,pydantic自动递归校验嵌套字段 - 字段设默认值(如
debug: bool = False)即表示可选;没默认值就是必填 - 若需校验 URL 格式,用
HttpUrl类型而非str,会自动检查协议和域名 - 注意:
pydantic v2的写法略有不同(BaseModel.model_validate(data)),别混用文档版本
不用第三方库?手写校验函数要注意嵌套键路径
如果项目禁用 pydantic,就得自己遍历字典。关键难点是处理 database.host 这类点分隔路径 —— 不能简单用 data.get("database.host"),因为字典不支持点号访问。
推荐写一个安全的 get_nested 工具函数:
def get_nested(d: dict, path: str, default=None):
keys = path.split(".")
for key in keys:
if isinstance(d, dict) and key in d:
d = d[key]
else:
return default
return d
<p>required_fields = ["database.url", "timeout"]
missing = [f for f in required_fields if get_nested(data, f) is None]
if missing:
raise ValueError(f"缺失必要字段:{missing}")
- 这个函数能正确处理中间某层为
None或非字典类型的情况,不会抛KeyError或TypeError - 别用
eval("data." + path)—— 有严重代码注入风险,且无法处理 key 含特殊字符(如短横线)的情况 - 如果字段允许空字符串但不允许缺失,校验逻辑要改成
is None or d == ""
校验通过后,为什么 data["timeout"] 还是字符串?
YAML 解析器不会自动类型转换:timeout: 30 解析为 int,但 timeout: "30" 就是 str。而 pydantic 的 int 字段能自动尝试转换("30" → 30),纯字典则不会。
- YAML 中数字不要加引号,除非你明确需要字符串语义
- 如果配置由非技术人员维护,建议在 Schema 层强制转换(
pydantic默认行为),而不是在业务代码里反复int(data["x"]) - 浮点数同理:
1.5是 float,"1.5"是 str,1,5(逗号小数点)在大多数 YAML 解析器里根本不是合法数字
字段完整性不只是“键存在”,更是“值类型符合预期”。这点容易被忽略,直到运行时报 TypeError: unsupported operand type(s) for +=: 'int' and 'str' 才发现。


















