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

example 1 - all strings use double quotes; example 2 - no strings (except the last two) use quotes; the YAML cookbook says: Enclosing strings in double quotes allows you to use escaping to represent ASCII and Unicode characters. Does this mean I need to use double quotes only when I want to escape some characters? If yes - why do they use double quotes everywhere in the first example - only for the sake of unity / stylistic reasons? the last two lines of example 2 use ! - the non-specific tag, while the last two lines of the first example don't - and they both work.

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

是否可以说:

一般来说,你不需要引用; 如果你想转义字符,请使用双引号; 使用!用单引号,当…? ! ?


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

In general, you don't need quotes. Use quotes to force a string, e.g. if your key or value is 10 but you want it to return a String and not a Fixnum, write '10' or "10". Use quotes if your value includes special characters, (e.g. :, {, }, [, ], ,, &, *, #, ?, |, -, <, >, =, !, %, @, \). Single quotes let you put almost any character in your string, and won't try to parse escape codes. '\n' would be returned as the string \n. Double quotes parse escape codes. "\n" would be returned as a line feed character. The exclamation mark introduces a method, e.g. !ruby/sym to return a Ruby symbol.

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

更新

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

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

在使用Docker开发Rails应用程序时,我就有这个顾虑。

我最喜欢的方法是一般不使用引号。这包括不使用引号:

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

然而,对于需要转换为字符串的整数值,我使用双引号:

Docker-compose类似版本:"3.8" 端口号如“8080:8080” 形象”traefik: v2.2.1”

但是,对于特殊情况,如布尔值、浮点数、整数和其他情况,在其中使用双引号的条目值可能被解释为字符串,请不要使用双引号。

下面是一个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

这是所有。

我希望这对你们有帮助


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

例如

foo: '{{ bar }}'

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

foo: barbaz{{ bam }}

不是,因为它不是以关键字符开始的。接下来,

foo: '123'

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

foo: bar1baz234
bar: 123baz

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

foo: 'yes'

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


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

It may sound subjective, but the number of rules you have to remember, if you want to use the quotes only when they are really needed as per the language spec, is somewhat excessive for such a simple thing as specifying one of the most common datatypes. Don't get me wrong, you will eventually remember them when working with YAML regularly, but what if you use it occasionally, and you didn't develop automatism for writing YAML? Do you really want to spend time remembering all the rules just to specify the string correctly?

“经验法则”的全部意义在于节省认知资源,无需思考就能处理一项普通任务。我们的“CPU”时间可以用来做比正确处理字符串更有用的事情。

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

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

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


如果你想在pytest tavern中转义字符串,!raw可以帮助你避免将字符串解析到yaml:

some: !raw "{test: 123}"

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


这个问题有一些很好的答案。 然而,我想扩展它们,并提供一些新的官方YAML v1.2.2规范(2021年10月1日发布)的背景,这是所有考虑YAML的“真正来源”。

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

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

双引号风格:

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

使用单引号风格:

单引号样式由周围的“指示器”指定。因此,在单引号标量中,这样的字符需要重复。这是在单引号标量中执行转义的唯一形式。特别是\和"字符可以自由使用。这将单引号标量限制为可打印字符。此外,只有在空格字符被非空格包围的长单引号行中才有可能换行。

朴素(未引用)风格:

The plain (unquoted) style has no identifying indicators and provides no form of escaping. It is therefore the most readable, most limited and most context sensitive style. In addition to a restricted character set, a plain scalar must not be empty or contain leading or trailing white space characters. It is only possible to break a long plain line where a space character is surrounded by non-spaces. Plain scalars must not begin with most indicators, as this would cause ambiguity with other YAML constructs. However, the :, ? and - indicators may be used as the first character if followed by a non-space “safe” character, as this causes no ambiguity.

博士TL;

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

只要适用,就使用不加引号的样式,因为它是最易读的。 如果字符串中使用了"和\这样的字符,则使用单引号样式('),以避免转义,从而提高可读性。 当前两个选项不充分时,例如在需要更复杂的换行符或需要不可打印字符的情况下,使用双引号样式(")。


下面是一个小函数(没有优化性能),它在需要时用单引号对字符串进行引用,并测试结果是否可以解编组为原始值: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
}

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,你不需要为布尔值使用引号。 只有版本需要报价。