YAML 因其简洁性和可读性,成为配置文件、数据序列化的热门选择。当需要处理包含换行符的长字符串(如段落文本、代码片段、日志信息等)时,YAML 提供了两种强大的标量块样式:字面块 (|)折叠块 (>)。它们允许你以更自然、易读的方式在 YAML 文件中编写多行文本。

1. 为什么需要多行块?

直接在 YAML 值中使用换行符通常会导致语法错误或解析困难。例如:

# 错误示例:直接换行不被允许(除非在引号内,但引号内换行处理复杂)
description: 这是第一行
    这是第二行 # 语法错误!

使用双引号 " 或单引号 ' 虽然可以包含换行符,但需要转义特殊字符(双引号内),并且处理内部引号和长文本时易读性较差:

# 使用双引号(可包含换行,但需要转义内部双引号和反斜杠,阅读困难)
description: "这是第一行。\n这是第二行,包含一个\"引号\"\n第三行。"

|> 解决了这些问题,它们:

  1. 提高可读性: 多行文本以其自然格式嵌入 YAML 文件。
  2. 简化书写: 无需使用 \n 显式表示换行。
  3. 保留或控制空白: 根据需要保留原始换行或将其折叠成空格。

2. 字面块 (Literal Block) - |

  • 符号: 使用竖线 | 后跟换行符开始。
  • 核心行为: 严格保留 输入文本中的所有换行符行尾的空白。文本块内的缩进和行首空白也会被保留
  • 结果: 解析后的字符串将包含你在 YAML 块中编写的所有换行符,格式与你在文件中看到的几乎完全一致。
  • 适用场景:
    • 需要精确保留格式的文本:代码片段、配置文件片段、ASCII 艺术、预格式化文本(如 Markdown/HTML 源码)。
    • 需要保留每一行末尾空白的文本(虽然不常见)。
    • 日志信息、错误消息等需要原样输出的多行文本。
  • 示例:
code_snippet: |
  def hello_world():
  print("Hello, World!")
  # 这是一个注释
  if True:
    print("YAML blocks are useful!")

# 解析后的值(Python 示例):
# "def hello_world():\n  print(\"Hello, World!\")\n  # 这是一个注释\n  if True:\n    print(\"YAML blocks are useful!\")\n"
# 注意:保留了所有缩进、换行和行尾的注释。

3. 折叠块 (Folded Block) - >

  • 符号: 使用右尖括号 > 后跟换行符开始。
  • 核心行为: 将文本块中的换行符替换为空格。但是,有一些重要的规则和例外:
    • 折叠规则: 连续的、非空行会被折叠成一个空格分隔的长行。
    • 保留空行: 空行(即完全没有任何字符,包括空格的行)会被保留,并作为换行符输出。空行是重要的分隔符。
    • 行尾反斜杠 \ 如果一行以非转义的反斜杠 \ 结尾,则该行不会折叠到下一行(即该换行符被保留),并且反斜杠会被移除(除非它被转义 \\)。
    • 行首空白: 块内的缩进和行首空白会被保留(与 | 相同)。
  • 结果: 解析后的字符串是一个“折叠”的文本段落。原本独立的非空行被合并成用空格连接的单行,但空行和以 \ 结尾的行会创建新的行。
  • 适用场景:
    • 长段落文本:如描述、文档、消息正文等,其中换行是为了文件可读性,但在实际使用中应作为连续文本(可能由空格分隔)。
    • 希望大部分内容为单行,但需要保留段落分隔(通过空行)。
    • 需要精确控制某些换行位置(通过行尾 \)。
  • 示例:
description: >
  This is the first sentence of a paragraph.
  This is the second sentence, which will be joined
  to the first with a space. It's much easier to read
  in the YAML file this way.

  This is a new paragraph because there was an empty
  line above. It will be separated by a newline.

  This line ends with a single backslash \
  so the next line starts on a new line. The backslash is removed.

# 解析后的值(Python 示例):
# "This is the first sentence of a paragraph. This is the second sentence, which will be joined to the first with a space. It's much easier to read in the YAML file this way.\n\nThis is a new paragraph because there was an empty line above. It will be separated by a newline.\nThis line ends with a single backslash so the next line starts on a new line. The backslash is removed.\n"
# 注意:
# - 第一段的所有非空行被折叠成一个长句(空格连接)。
# - 两个空行(在YAML块中)变成了两个换行符 `\n\n`,分隔段落。
# - 以 `\` 结尾的行:反斜杠被移除,该行与下一行之间的换行符被保留(即 `\n` 出现在 `backslash` 和 `so` 之间)。

4. 关键区别总结 (| vs >)

特性 |(字面量块) >(折叠块)
换行保留 保留所有换行 将换行转换为空格(除非是空行)
最终结果 多行字符串(带换行) 单行或段落式字符串(带空格)
适合内容 脚本、配置段、代码块 描述文字、段落性说明文本
阅读体验 更易于阅读格式化内容 更紧凑,节省行数
尾部换行控制 支持 |-, |+ 支持 >-, >+
空行处理 原样保留 保留段落(空行)分隔

5. 共同规则与注意事项

  1. 缩进 (Indentation):
    • 块内容必须相对于块指示符 (|>) 统一缩进。这个缩进级别定义了“块内容的起始列”。
    • YAML 解析器会剥离这个公共缩进前缀。块内容中每一行开头的、与公共缩进级别相同的空格会被移除。
    • 公共缩进级别之后的缩进(即块内容内部的额外缩进)会被保留
    • 重要: 块内所有行的缩进必须大于或等于块指示符的缩进级别。否则会导致解析错误。
  2. 块结尾与尾随换行符:
    • 块由一个缩进级别小于块内容起始缩进级别的行结束,或者由文档结束结束。
    • 默认情况下,YAML 解析器会在解析后的字符串末尾添加一个换行符 (\n)。这是符合 POSIX 标准的常见行为(文本文件以换行符结尾)。
    • 可以使用块指示符修饰符控制这个行为:
    • |>:默认行为,保留块末尾的换行符(即添加一个 \n)。
    • |->-剥离 (strip) 块末尾的换行符。解析后的字符串不包含最后一个换行符。
    • |+>+保留 (keep) 块末尾的所有换行符。如果块结束时有多行空行,它们都会被保留。 * 示例: ```yaml with_newline: | # 默认,等价于 | 或 > Line 1 Line 2

      注意这里下面还有一个空行

    stripped: |- Line 1 Line 2

    kept: |+ Line 1 Line 2

    解析结果:

    with_newline: “Line 1\nLine 2\n\n” (末尾有1个换行符,来自最后那个空行,再加上默认添加的一个?注意:规范是块内容结束行后的空行也会影响,具体实现可能略有差异,通常 | 会保留内容中所有行包括末尾空行)

    stripped: “Line 1\nLine 2” (无末尾换行符)

    kept: “Line 1\nLine 2\n\n” (保留了内容末尾的两个换行符)

    ```

    • 关于尾随换行符的具体处理,YAML 规范有详细定义,不同解析器实现可能稍有差异,但 -+ 的意图是明确的。最安全的做法是明确使用 -+ 来表达你的需求。
  3. 空格与制表符: YAML 通常推荐使用空格进行缩进。虽然规范允许制表符 (\t),但强烈建议避免使用制表符,因为不同环境对制表符宽度的解释不同,极易导致缩进错误和解析问题。坚持使用空格(通常是 2 个或 4 个)是最佳实践。
  4. 转义字符:|> 块内,大多数字符都是字面量,不需要转义。双引号 "、单引号 '、反斜杠 \ 等都可以直接书写(除了在折叠块 > 中行尾的 \ 有特殊含义)。这使得包含这些字符的文本(如 HTML、SQL)写起来非常方便。例如,在 | 块中写 "This is a quote" 完全没问题。
  5. 与引号标量的对比: |> 提供了比双引号 " 或单引号 ' 更清晰、更适合处理真正多行且需要控制空白的文本的方式。引号标量更适合单行字符串或在单行内需要转义/处理特殊字符的场景。

6. 如何选择?(| vs >)

  • 选择 | (字面块) 当:
    • 你需要完全保留原始格式、所有换行符和行尾空白
    • 内容本质上是代码、结构化文本或预格式化内容
    • 换行符具有语义意义(例如,每行代表一个列表项、一个命令、一个日志条目)。
  • 选择 > (折叠块) 当:
    • 你的内容是自然语言段落
    • 文件中的换行主要是为了提高 YAML 文件自身的可读性,你希望这些换行在解析后变成空格(使文本更连贯)。
    • 你仍然需要通过空行来表示段落分隔
    • 你需要偶尔精确控制某个换行位置(使用行尾 \)。

7. 高级技巧

  • 结合使用: 一个 YAML 文档中可以自由混合使用 |> 块,以及其他标量样式(普通、单引号、双引号)。
  • 嵌套结构: 块标量可以嵌套在映射(键值对)的值中或序列(列表)的项中。
  • 明确意图: 总是使用 -+ 修饰符来明确你对尾随换行符的期望 (|, |-, |+, >, >-, >+),避免歧义。

YAML 的 | (字面块) 和 > (折叠块) 是处理多行字符串的强大工具,它们显著提升了配置文件和复杂数据结构的可读性和可维护性。理解它们之间的核心区别——| 严格保留换行,> 折叠换行为空格(但保留空行和 \ 行)——以及缩进、尾随换行控制等共同规则,是有效使用 YAML 的关键。根据你的文本内容是强调精确格式还是自然语言流,明智地选择 |>,能让你的 YAML 文件既整洁又准确地表达意图。


孟斯特

声明:本作品采用署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)进行许可,使用时请注明出处。
Author: mengbin
blog: mengbin
Github: mengbin92
腾讯云开发者社区:孟斯特