C++ 中的 YAML 解析器

2025年5月19日 | 阅读10分钟

YAML 的缩写是 YAML Ain't Markup Language(YAML 不是标记语言),常用于数据序列化。它易于阅读和编写。与 JSON 或 XML 等其他格式不同,YAML 更注重简洁性。因此,它被用于配置文件、系统间数据交换以及注重可读性的场合。YAML 是一种简洁的语言,没有诸如大括号和逗号之类的奇怪字符,这使得这种语言的设计非常直观。

  • YAML 是一种同时能被人类和机器理解的结构化表达技术。在 DevOps 等领域,与 KubernetesDockerAnsible 等工具结合使用时,主要利用 YAML 进行配置。它具有通用性强的优点,能够轻松地在应用程序中表示简单和复杂的数据模型;这些模型包括但不限于分层列表和字典。
  • C++ 中,YAML 在处理配置文件、结构化数据或其他应用程序时非常有用。与其编写自己的配置文件解析器或使用不再人类可读的属性文件,YAML 是处理数据的标准解决方案。C++ 采用现代编程范式,能够很好地适应 C++ 应用程序,用于设置存储、数据交换和处理应用程序状态。
  • YAML 在 C++ 开发中的核心价值在于其简洁、无冗余的语言特性。例如,它允许开发人员以有限的冗余来建模复杂结构,而这在 JSON 或 XML 中是常见问题。此外,其易于理解的结构使其灵活易用,不仅仅限于开发人员,也便于非技术人员进行应用程序调整和 YAML 文件调试。
  • YAML 还有许多优点,例如其提供的锚点(anchor)和别名(alias)功能,可以用来表示任何复杂的关联。这些功能使得 YAML 文件的配置可重用、高效且避免了在不同配置中重复数据。对于开发大型应用程序的开发人员来说,这在配置管理方面非常有用,而不是成为错误的根源。
  • 然而,YAML 的书写也有其局限性,因为它使用缩进来使文件易于人类阅读。在此过程中也可能出错,特别是如果没有得到妥善处理。事实上,诸如缩进不当、使用空格而非制表符(反之亦然)等情况会导致解析失败,并且正确的诊断可能非常困难。此外,YAML 可以用不同的方式表达相同的指令,问题在于当使用不同的工具或库处理 YAML 文档时,这些版本可能不兼容。

因此,YAML 是数据序列化和配置存储的更好、更有效的解决方案,其语法提供了与当前软件开发方法相匹配的清晰结构。对于 C++ 开发人员来说,它提供了一种可能更快地处理结构化数据的方法,同时有望带来更好的协作并减少代码的复杂性。

理解 YAML 语法

YAML 的基本方面在于其自然清晰的结构,这使其非常吸引人。YAML 文件是缩进的,缩进级别定义了数据项之间的关系。这种基于缩进的方法意味着无需使用 JSON 或 XML 等格式中常见的括号、引号或其他分隔符,这使得解决方案看起来整洁明了。

缩进

缩进是 YAML 格式化的基础。它声明了文件中结构化方面的组织,并定义了它们在文件中的位置。缩进意味着比某个设置更深层级的所有数据都比后者缩进更多。例如,表示用户配置文件的 YAML 文件可能如下所示:

在此示例中,abstract object address 由 street、city 和 zip 组成,由行缩进分隔。缩进必须始终一致;否则,缩进的差异会导致解析器出现问题,因为 YAML 解析器在解释结构时对缩进很敏感。

键值对

YAML 中的数据表示在键和值或属性和值方面很强大。字典的键部分始终是字符串,而值部分可以是任何数据类型,包括字符串、整数和其他结构。例如:

在这里,language 和 version 是键,它们的值分别是“C++”和 17。在分层级别中,键应唯一或重叠较少,以最大限度地减少因使用相同或相似术语而可能产生的混淆。

列表

YAML 支持列表,它们最好通过使用连字符(-)来表示。列表可以只包含基本类型,如字符串或数字,也可以包含更复杂的类型,如对象或甚至其他列表。例如:

此列表反映的框架规定了做事的方式,尤其是在组织中。列表还可以嵌套,以方便开发人员表示分层信息。

注释

YAML 的一个关键特性是注释,使用“#”符号是实现注释的最常见方式。它允许在 YAML 文件中包含注释,这使得在团队协作的上下文中处理文件更加容易,因为不同的员工可以处理配置文件。例如:

锚点和别名

YAML 中一个突出的特点是锚点和别名的使用,这使得重复数据成为可能。锚点(&)允许我们保存一个数据块,而别名(*)帮助我们在同一个文件中引用它。它减少了项目配置信息的依赖性和冗余,并保持了配置之间的一致性。例如:

在这种情况下,production 块扩展了 default_settings: retries 的值,但覆盖了 timeout。

多行字符串

YAML 被设计成在多行 字符串 的表示方式上非常灵活;多行字符串使用管道符(|)或大于号(>)定义。管道符保留换行符,而大于号将行折叠成一个字符串。

C++ 中的 YAML 解析器如何工作?

YAML 解析器 在读取 YAML 文件并将人类可读的标记语言转换为 C++ 中可编程的形式方面发挥着至关重要的作用。从根本上说,YAML 解析器负责将给定的 YAML 文本分解为语法组件,然后将所引用的语法与合适的 C++ 数据结构关联起来。这个过程包括几个步骤,以确保在 C++ 应用程序中正确读取、验证和使用 YAML 系统中的信息。

正如语法分析过程在语法上下文中的作用一样,YAML 文件的解析过程从词法分析步骤开始。这些块包括带有键值对的对象、列表项以及块语句的缩进级别;列表元素是标记。在标记之后,流程转向将标记布局成某种结构化格式,最可能是一个抽象语法树(AST)。这棵树反映了 YAML 文件中的分层自底向上关系,这使得解析器能够理解诸如字典和列表之类的结构。

  • 构建完语法树后,解析器会将 YAML 数据映射到 C++ 结构中,例如 std::map 或其他基于模板的容器,将键值数据结构(类似于 Python 字典)转换为 C++ 的类型系统。对于列表,它通常使用 std::vector,但对于更专业的用例,它可以使用 std::array、std::deque 或其他容器,具体取决于生成代码所需的目标容器。这个称为数据映射的步骤是使开发人员能够安全有效地处理 YAML 数据的基础。此外,YAML 标准中用于“指向”数据内部的锚点和别名也需要由解析器进行处理。这些功能对解析器提出了很高的要求,因为必须解析引用以避免对 StatsML 获得的数据产生负面影响。
  • YAML 解析中的最后一个但非常重要的概念是错误处理。YAML 文件是人们容易理解的格式。但是,它容易出现诸如制表符错误或其他字符错误等问题。因此,当这些错误被传递给解析器之前,一个好的解析器会在解析过程中检测到它们,并向开发人员提供有助于纠正问题的错误消息。此解析器还会根据预定义的规则检查 YAML 数据,以确保解析的数据对于特定应用程序是正确的。
  • 当前 C++ 的 YAML 解析器旨在用于解析一系列常见的现代任务,例如配置文件管理、数据表示和数据序列化。在简化最佳 YAML 功能的同时,它们有助于设定主要应用程序优先级并利用 YAML 的优势,如清晰的语法和灵活性。

C++ 中流行的 YAML 解析器

几乎所有的 C++ 开发人员在处理 YAML 数据时都有多种选择,因为有许多 YAML 解析器可供使用,它们提供极其可靠且易于使用的 API。这些库可以满足任何需求,从简单的 YAML 文件解析到复杂的锚点和复合结构的利用。最广泛使用的库可以分为以下几类:YAML-CPP、libyaml 和 Boost.PropertyTree,它们各有优点,具体取决于特定应用程序。

目前 YAML-CPP 是 C++ 中最常用且最好的 YAML 解析库。它是一个开源项目,提供易于使用的 API 来解析/读取/写入 YAML 文档。它目前支持基本的 YAML 构造,包括键值对、列表和嵌入式结构,以及锚点和别名构造。其用户友好的 API 使开发人员能够轻松地将 YAML 文件加载到本地 C++ 数据结构(如 std::map 或 std::vector)中,并轻松获取该集合的值。此外,YAML-CPP 还配备了各种错误描述,例如语法错误或缺失的键。尽管它不是最“闪电般快速”的库,但它提供了丰富的 数组 功能且易于使用,因此成为大多数 C++ 项目的标准。

但是,对于需要高性能和内存效率的目标的应用,还有 libyaml。Libyaml 是一个快速轻量级的解析器。它是一个用 C 语言实现的底层库。它提供了一个极其基础的接口,为开发人员提供了极大的控制权,可以按自己意愿处理 YAML 数据。与 YAML-CPP 相比,libyaml 没有额外的包装,因此开发人员必须自行实现基本操作,如类型转换和嵌套结构的 [处理]。然而,其面向性能的方法在需要立即解析大型 YAML 文件或可用资源受限的情况下可能非常有用。

代码:使用 YAML-CPP 解析和修改 YAML

输出

Terminal:
Server Host: 127.0.0.1
Server Port: 8080
Database Name: test_db
Database User: admin
Database Password: secret
Database Retries: 3
Enabled Features:
 - logging
 - caching
 - backup
Added 'analytics' to features.
Modified YAML saved to 'modified_config.yaml'.
Modified YAML File (modified_config.yaml)
server:
  host: "127.0.0.1"
  port: 8080
database:
  name: "test_db"
  user: "admin"
  password: "secret"
  retries: 3
features:
  - logging
  - caching
  - backup
  - analytics   

结论

然而,用 C++ 编写的YAML 解析器一直存在短缺。这种 对象 对于配置管理、数据交换以及使用人类可读数据格式(YAML)的代理至关重要。所有这些解析器都有助于解析或处理 YAML 数据,将看起来像语法数据的东西呈现为读写 API。通过将 YAML 内容转换为本地 C++ 数据结构,例如 std::map 和 std::vector,这些结构类似于其他数据结构或对象,允许开发人员处理形成分层或嵌套结构的数据对象。

在 C++ 中使用 YAML 的机会很少且多样,其中我们可以提到 YAML-CPP、libyaml 和 Boost.PropertyTree。YAML-CPP 比大多数其他选项更受欢迎,因为 YAML-CPP 具有更多功能,并且操作起来更简单。但是,libyaml 在高负载应用程序的上下文中很有用,如果我们只需要支持多种格式的基本 YAML,Boost.PropertyTree 将非常适合我们。每个库都服务于不同的需求。因此,在大多数情况下,仍然有可能实现高度灵活的度量。

出于这个原因,YAML 解析器可以从词法分析点一直学习到数据映射点。此外,结合使用 YAML 的实际场景和不同的错误处理策略,YAML 解析器简化了 YAML 的使用,同时保持了高质量的代码和高性能。在当代软件开发过程中,它们的作用日益凸显,是 C++ 开发人员手中重要的工具。