
使用 yaml.safe_load() 解析含换行和缩进的字符串时,默认会折叠多余空白(包括行首空格),导致格式丢失;通过 YAML 的字面量块标量(| 或 |-)可完整保留原始换行与空格。
使用 `yaml.safe_load()` 解析含换行和缩进的字符串时,默认会折叠多余空白(包括行首空格),导致格式丢失;通过 yaml 的字面量块标量(`|` 或 `|-`)可完整保留原始换行与空格。
在 PyYAML 中,safe_load() 对普通双引号或无引号的字符串采用“折叠样式”(folded style)解析规则:它会将连续空白(尤其是行首缩进)规范化,甚至移除空行及前导空格。例如:
import yaml
data = yaml.safe_load("first\n\n second")
print(repr(data)) # 输出: 'first\nsecond' —— 空行被压缩,缩进空格完全丢失
这并非 bug,而是 YAML 规范对隐式字符串的默认行为(参见 YAML 1.2.2 规范 §8.1.2)。要精确保留换行、空行和行首空格,必须显式使用字面量块标量(literal block scalar),即以 |(保留末尾换行)或 |-(剥离末尾空白行)开头的多行结构。
✅ 正确做法:将原始字符串转换为符合 YAML 字面量语法的格式:
import yaml
# 使用 |- 表示字面量块(strip trailing empty lines)
data = yaml.safe_load("|-\n first\n second")
print(repr(data)) # 输出: 'first\n second'
# 若需保留末尾换行,用 |(不带 -)
data_with_trailing = yaml.safe_load("|\n first\n second\n")
print(repr(data_with_trailing)) # 输出: 'first\n second\n'
⚠️ 注意事项:
- 字面量块要求每行内容必须严格缩进(至少比 | 所在行多一个空格),且缩进必须一致(推荐使用空格,避免混用 Tab);
- | 和 |- 后需紧跟换行符,其后第一行的内容缩进将作为基准,后续各行的相对缩进会被保留;
- 不要对字面量块内文本额外加引号或转义——YAML 会原样读取;
- 若输入是动态生成的字符串,建议封装工具函数自动转换为字面量格式,例如:
def literal_yaml(s: str) -> str:
"""将任意字符串转为安全的 YAML 字面量块表示"""
if not s:
return "| ''"
lines = s.splitlines(keepends=True)
# 确保首行有缩进(避免解析错误)
indented = ''.join(' ' + line for line in lines)
return f"|-\n{indented}"
# 示例
raw = "first\n\n second"
yaml_str = literal_yaml(raw)
result = yaml.safe_load(yaml_str)
assert result == raw # ✅ 断言通过
总结:PyYAML 的 safe_load 并非“删除空格”,而是遵循 YAML 规范对不同标量风格的语义处理。当需要保真还原原始文本格式(如配置模板、代码片段、日志段落等),务必主动使用 | / |- 字面量语法——这是 YAML 原生支持的、最可靠的方式。











