590

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

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

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

是不是可以这么说:

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

7 回答 7

803

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

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

在我看来,最好的方法是不使用引号,除非你必须这样做,然后使用单引号,除非你特别想处理转义码。

更新

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

en:
  yesno:
    'yes': 'Yes'
    'no': 'No'
于 2014-03-06T20:19:37.513 回答
33

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

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

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

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

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

这些只是一些偶尔使用 YAML 用户要记住的两条规则,最大限度地减少认知工作。

于 2021-05-14T14:44:48.017 回答
20

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

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

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

双引号样式

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

单引号样式

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

普通(未引用)样式

  • 普通(不带引号)样式没有标识指示符,也没有提供任何形式的转义。因此,它是最易读、最有限和最上下文敏感的风格。除了受限字符集之外,纯标量不得为空或包含前导或尾随空白字符。只能在空格字符被非空格包围的地方打断一条长的平线。大多数指标不能以纯标量开头,因为这会导致与其他 YAML 结构的歧义。但是,如果后跟非空格“安全”字符,则:?指示符-可以用作第一个字符,因为这不会引起歧义。

TL;博士

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

  • 只要适用,就使用不带引号的样式,因为它是最易读的。
  • 如果字符串中使用了和等字符,请使用单引号样式 ( ')以避免转义它们,从而提高可读性。"\
  • "当前两个选项不够用时,即在需要更复杂的换行符或需要不可打印字符的情况下,请使用双引号样式 ( )。
于 2021-11-05T08:51:11.317 回答
10

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

例如

foo: '{{ bar }}'

需要引号,因为它可能被误解为 datatype dict,但是

foo: barbaz{{ bam }}

不是,因为它不是以关键 char 开头。下一个,

foo: '123'

需要引号,因为它可能被误解为 datatype 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 中使用括号一样。这是不好的做法,会损害可读性,并抛弃不必引用字符串的美丽功能。

于 2021-03-19T09:19:14.557 回答
2

在使用Docker处理Rails应用程序时,我有这个担忧。

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

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

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

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

但是,对于特殊情况,如booleans, floats,integers和其他情况,使用双引号作为输入值可能会被解释为strings,请不要使用双引号。

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

就这样。

我希望这有帮助

于 2020-05-26T14:29:48.900 回答
0

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

some: !raw "{test: 123}"

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

于 2021-09-15T22:01:16.880 回答
0

这是一个小函数(未针对性能进行优化),如果需要,它会用单引号引用您的字符串,并测试结果是否可以解组为原始值: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
}
于 2021-11-26T12:10:07.597 回答