YAML:YAML 中的字符串需要引号吗?

YAML: Do I need quotes for strings in YAML?

提问人:Alexander Popov 提问时间:10/1/2013 最后编辑:Promise PrestonAlexander Popov 更新时间:10/27/2022 访问量:449114

问:

我正在尝试编写一个 YAML 字典,用于 Rails 项目的国际化。不过我有点困惑,因为在某些文件中,我看到双引号中的字符串,而在一些文件中则没有。需要考虑的几点:

  • 示例 1 - 所有字符串都使用双引号;
  • 示例 2 - 没有字符串(最后两个除外)使用引号;
  • YAML 说明书说: 将字符串括在双引号中允许您使用转义来表示 ASCII 和 Unicode 字符。这是否意味着我只有在想要转义某些字符时才需要使用双引号?如果是 - 为什么他们在第一个示例中到处都使用双引号 - 只是为了统一/风格原因?
  • 示例 2 的最后两行使用 - 非特定标记,而第一个示例的最后两行不使用 - 并且它们都有效。!

我的问题是:在 YAML 中使用不同类型的引号的规则是什么?

可以说:

  • 一般来说,你不需要引号;
  • 如果要转义字符,请使用双引号;
  • 与单引号一起使用,当... ?!?!
语法 YAML 引号

评论

6赞 heroin 12/21/2017
第二个链接不再起作用,我建议将您的示例放入问题中。

答:

1007赞 Mark Berry 3/7/2014 #1

在简要回顾了问题中引用的 YAML 食谱并进行了一些测试之后,以下是我的解释:

  • 一般来说,你不需要引号。
  • 使用引号强制字符串,例如,如果你的键或值是,但你希望它返回一个 String 而不是一个 Fixnum,write 或 .10'10'"10"
  • 如果值包含特殊字符,请使用引号(例如,、、、:{}[],&*#?|-<>=!%@\
  • 单引号允许您在字符串中放置几乎任何字符,并且不会尝试解析转义码。 将作为字符串返回。'\n'\n
  • 双引号解析转义码。 将作为换行符返回。"\n"
  • 感叹号引入了一种方法,例如 返回 Ruby 符号。!ruby/sym

在我看来,最好的方法是除非必要,否则不要使用引号,然后使用单引号,除非您特别想处理转义码。

更新

“Yes”和“No”应用引号(单引号或双引号)括起来,否则它们将被解释为 TrueClass 和 FalseClass 值:

en:
  yesno:
    'yes': 'Yes'
    'no': 'No'

评论

34赞 Adam Spiers 5/28/2015
这还不是全貌。例如,和 ' 可以在纯字符串中的任何位置使用,但开头除外,因为它们是保留指示符@
37赞 Mark Berry 8/2/2015
我不是想提供全貌,只是一些经验法则。是的,看起来有时,一些特殊字符(保留指标)可以在没有引号的情况下使用(只要保留指标不启动普通标量),但每当您看到特殊字符时使用引号并没有错。
51赞 Steve Bennett 12/1/2015
YAML 中字符串的规则非常复杂,因为有很多不同类型的字符串。我在这里写了一张表:stackoverflow.com/questions/3790454/......
119赞 Vicky Chijwani 2/17/2017
鉴于所有这些警告,我宁愿到处使用引号:-/
7赞 tinita 7/8/2018
另外,这是我写的一个非常完整的参考资料:blogs.perl.org/users/tinita/2018/03/......
3赞 Promise Preston 5/26/2020 #2

我在使用 Docker 开发 Rails 应用程序时遇到了这个问题。

我最喜欢的方法是通常使用引号。这包括对以下项使用引号:

  • 变量,如${RAILS_ENV}
  • 用冒号分隔的值 (:) 喜欢postgres-log:/var/log/postgresql
  • 其他字符串值

但是,我对需要转换为字符串的值使用双引号,例如:integer

  • docker-compose 版本,如version: "3.8"
  • 端口号,例如"8080:8080"
  • 图像"traefik:v2.2.1"

但是,对于 、 、 等特殊情况,对条目值使用双引号可能会被解释为 ,请不要使用双引号。booleansfloatsintegersstrings

下面是一个示例文件来解释此概念:docker-compose.yml

version: "3"

services:
  traefik:
    image: "traefik:v2.2.1"
    command:
      - --api.insecure=true # Don't do that in production
      - --providers.docker=true
      - --providers.docker.exposedbydefault=false
      - --entrypoints.web.address=:80
    ports:
      - "80:80"
      - "8080:8080"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro

就这样。

我希望这会有所帮助

评论

4赞 Smart Manoj 10/24/2020
violates - 如果值在另一个答案中包含“:”,则使用引号
23赞 user657127 3/19/2021 #3

yaml 中的字符串仅在值(开头)可能被误解为数据类型或值包含“:”(因为它可能被误解为键)时才需要引号。

例如

foo: '{{ bar }}'

需要引号,因为它可能被误解为 数据类型 ,但是dict

foo: barbaz{{ bam }}

不会,因为它不是以临界字符开头的。下一个

foo: '123'

需要引号,因为它可能被误解为 数据类型 ,但是int

foo: bar1baz234
bar: 123baz

不会,因为它不能被误解为int

foo: 'yes'

需要引号,因为它可能被误解为数据类型bool

foo: "bar:baz:bam"

需要引号,因为该值可能会被误解为键。

这些只是例子。使用有助于避免使用错误的标记开始值yamllint

foo@bar:/tmp$ yamllint test.yaml 
test.yaml
  3:4       error    syntax error: found character '@' that cannot start any token (syntax)

如果有效地使用 YAML,则必须这样做。

正如一些人所建议的那样,引用所有字符串就像在 python 中使用括号一样。这是一种不好的做法,损害了可读性,并抛弃了不必引用字符串的美丽功能。

评论

2赞 Mark Berry 3/21/2021
谢谢你的例子。看来我们同意;正如我在回答中所说:“最好的方法是不要使用引号,除非你不得不这样做。关于您有用的数据类型规则的问题:您是否像 OP 的问题一样专门指 Ruby on Rails 中的 YAML?数据类型解释似乎可能因编程语言而异。
1赞 10/29/2021
@MarkBerry 感谢您的输入。是的,对我来说,一般规则也是:除非有必要,否则不要引用。是的,你正确地观察到,我使用了 Python 而不是 Ruby 的示例。我是故意这样做的。突出显示抽象消息:1) 使用 linter 2) Yaml 不绑定到一种语言,而是一种语言。这就是我使用“键:值”术语的原因。
90赞 wombatonfire 5/14/2021 #4

虽然 Mark 的回答很好地总结了根据 YAML 语言规则何时需要引号,但我认为许多开发人员/管理员在使用 YAML 中的字符串时会问自己“处理字符串的经验法则是什么?

这听起来可能很主观,但是如果您只想在根据语言规范真正需要引号时才使用引号,那么您必须记住的规则数量对于指定最常见的数据类型之一这样简单的事情来说有点过多。不要误会我的意思,当你经常使用 YAML 时,你最终会记住它们,但如果你偶尔使用它,并且你没有开发编写 YAML 的自动化呢?你真的想花时间记住所有规则只是为了正确指定字符串吗?

“经验法则”的全部意义在于节省认知资源,并在不考虑的情况下处理共同的任务。我们的“CPU”时间可以说可以用于比正确处理字符串更有用的事情。

从这个纯粹实用的角度来看,我认为最好的经验法则是单引字符串。其背后的基本原理:

  • 单引号字符串适用于所有方案,但需要使用转义序列的情况除外。
  • 在单引号字符串中必须处理的唯一特殊字符是单引号本身。

对于一些偶尔的 YAML 用户来说,这些只是要记住的 2 条规则,从而最大限度地减少认知工作。

评论

1赞 Jordan Gee 11/20/2021
我喜欢这个答案。我认为 YAML 的全部意义在于保持简单。然而,在这里我正在寻找为什么 sizeInBytes 的 int 值的答案:12345678必须在我最新的 YAML b/c 中“引用”,显然想要有一个字符串配置属性(可能?--但我实际上仍然不知道答案。
4赞 acmoune 5/24/2022
一个更简单的方法是:对字符串使用双引号。
2赞 wombatonfire 5/24/2022
@acmoune 我不确定双引号是否是最佳的默认选项,因为它们对转义序列的自动解释可能不是您所期望的。不过,如果你总是记得它,如果你特别需要它,那么是的,为什么不呢。
0赞 Max Tkachenko 9/16/2021 #5

如果您尝试在 pytest tavern 中转义字符串,避免将字符串解析为 yaml 可能会有所帮助:!raw

some: !raw "{test: 123}"

查看更多信息: https://tavern.readthedocs.io/en/latest/basics.html#type-conversions

68赞 F1ko 11/5/2021 #6

这个问题已经有一些很好的答案。 但是,我想扩展它们并提供一些来自新的官方 YAML v1.2.2 规范(2021 年 10 月 1 日发布)的上下文,该规范是考虑 YAML 的所有事物的“真正来源”。

有三种不同的样式可用于表示字符串,每种样式都有自己的(缺点)优点:

YAML 提供三种流标量样式:双引号、单引号和普通(未引号)。每个都提供了可读性和表现力之间的不同权衡。

双引号样式

  • 双引号样式由周围的指示符指定。这是唯一能够使用转义序列来表示任意字符串的样式。这是以必须逃避 and 字符为代价的。"\\"

单引号样式

  • 单引号样式由周围的指标指定。因此,在单引号标量中,需要重复此类字符。这是在单引号标量中执行的唯一转义形式。特别是,可以自由使用 and 字符。这会将单引号标量限制为可打印字符。此外,只能断开空格字符被非空格包围的长单引号行。'\"

普通(未加引号)样式

  • 普通(未加引号)样式没有识别指标,也不提供任何形式的转义。因此,它是可读性最强、最受限制和最上下文敏感的样式。除了受限制的字符集外,普通标量不得为空或包含前导或尾随空格字符。只能在空格字符被非空格包围的长普通线中断开。 普通标量不得以大多数指标开头,因为这会导致与其他 YAML 构造产生歧义。但是,如果后面跟着一个非空格“安全”字符,则 和 指示符可以用作第一个字符,因为这不会造成歧义。:?-

TL的;博士

话虽如此,根据官方 YAML 规范,应该

  • 在适用的情况下,请使用不带引号的样式,因为它是最易读的。
  • 如果字符串内部使用了 and 等字符,请使用单引号样式 (),以避免转义它们,从而提高可读性。'"\
  • 当前两个选项不够用时,请使用双引号样式 (),即在需要更复杂的换行符或需要不可打印字符的情况下。"

评论

1赞 Mark Berry 3/3/2022
感谢您的总结。它涉及到如何描绘空白,这是我在回答中没有考虑过的。但它省略了引号的主要决定因素之一:当默认值为其他数据时,我是否要强制数据类型为字符串。第 2.4 节中对此进行了简要介绍:“在 YAML 中,未标记的节点根据应用程序被赋予一种类型。最简单的例子 2.21 显示 .该部分还涵盖了我不知道存在的更复杂、更显式的类型!string: '012345'
0赞 Philzen 10/23/2023
一些额外的警告:普通标量不得包含 和 ' #' 字符组合。此外,在流集合内部或用作隐式键时,纯标量不得包含 []{} 字符。
0赞 Philzen 10/23/2023
我不明白的是:为什么根据规范允许在纯文本的开头 - 这不会使它成为参考吗?&
0赞 andig 11/26/2021 #7

下面是一个小函数(未针对性能进行优化),如果需要,它会用单引号引用字符串,并测试结果是否可以解组为原始值:https://go.dev/play/p/AKBzDpVz9hk。 它不是测试规则,而是使用封送器本身并检查封送和未封送的值是否与原始版本匹配。

func yamlQuote(value string) string {
    input := fmt.Sprintf("key: %s", value)

    var res struct {
        Value string `yaml:"key"`
    }

    if err := yaml.Unmarshal([]byte(input), &res); err != nil || value != res.Value {
        quoted := strings.ReplaceAll(value, `'`, `''`)
        return fmt.Sprintf("'%s'", quoted)
    }

    return value
}
-1赞 seunggabi 6/28/2022 #8
version: "3.9"

services:
  seunggabi:
    image: seunggabi:v1.0.0
    command:
      api:
        insecure: true
    ports:
      - 80:80
      - 8080:8080
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
docker compoese up docker-compose.yaml

如果使用 ,则不需要对布尔值使用引号。
只有版本需要报价。
docker compose v2