提问人:Alexander Popov 提问时间:10/1/2013 最后编辑:Promise PrestonAlexander Popov 更新时间:10/27/2022 访问量:449114
YAML:YAML 中的字符串需要引号吗?
YAML: Do I need quotes for strings in YAML?
问:
我正在尝试编写一个 YAML 字典,用于 Rails 项目的国际化。不过我有点困惑,因为在某些文件中,我看到双引号中的字符串,而在一些文件中则没有。需要考虑的几点:
- 示例 1 - 所有字符串都使用双引号;
- 示例 2 - 没有字符串(最后两个除外)使用引号;
- YAML 说明书说: 将字符串括在双引号中允许您使用转义来表示 ASCII 和 Unicode 字符。这是否意味着我只有在想要转义某些字符时才需要使用双引号?如果是 - 为什么他们在第一个示例中到处都使用双引号 - 只是为了统一/风格原因?
- 示例 2 的最后两行使用 - 非特定标记,而第一个示例的最后两行不使用 - 并且它们都有效。
!
我的问题是:在 YAML 中使用不同类型的引号的规则是什么?
可以说:
- 一般来说,你不需要引号;
- 如果要转义字符,请使用双引号;
- 与单引号一起使用,当... ?!?
!
答:
在简要回顾了问题中引用的 YAML 食谱并进行了一些测试之后,以下是我的解释:
- 一般来说,你不需要引号。
- 使用引号强制字符串,例如,如果你的键或值是,但你希望它返回一个 String 而不是一个 Fixnum,write 或 .
10
'10'
"10"
- 如果值包含特殊字符,请使用引号(例如,、、、
:
{
}
[
]
,
&
*
#
?
|
-
<
>
=
!
%
@
\
- 单引号允许您在字符串中放置几乎任何字符,并且不会尝试解析转义码。 将作为字符串返回。
'\n'
\n
- 双引号解析转义码。 将作为换行符返回。
"\n"
- 感叹号引入了一种方法,例如 返回 Ruby 符号。
!ruby/sym
在我看来,最好的方法是除非必要,否则不要使用引号,然后使用单引号,除非您特别想处理转义码。
更新
“Yes”和“No”应用引号(单引号或双引号)括起来,否则它们将被解释为 TrueClass 和 FalseClass 值:
en:
yesno:
'yes': 'Yes'
'no': 'No'
评论
我在使用 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
就这样。
我希望这会有所帮助
评论
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 中使用括号一样。这是一种不好的做法,损害了可读性,并抛弃了不必引用字符串的美丽功能。
评论
虽然 Mark 的回答很好地总结了根据 YAML 语言规则何时需要引号,但我认为许多开发人员/管理员在使用 YAML 中的字符串时会问自己“处理字符串的经验法则是什么?
这听起来可能很主观,但是如果您只想在根据语言规范真正需要引号时才使用引号,那么您必须记住的规则数量对于指定最常见的数据类型之一这样简单的事情来说有点过多。不要误会我的意思,当你经常使用 YAML 时,你最终会记住它们,但如果你偶尔使用它,并且你没有开发编写 YAML 的自动化呢?你真的想花时间记住所有规则只是为了正确指定字符串吗?
“经验法则”的全部意义在于节省认知资源,并在不考虑的情况下处理共同的任务。我们的“CPU”时间可以说可以用于比正确处理字符串更有用的事情。
从这个纯粹实用的角度来看,我认为最好的经验法则是单引字符串。其背后的基本原理:
- 单引号字符串适用于所有方案,但需要使用转义序列的情况除外。
- 在单引号字符串中必须处理的唯一特殊字符是单引号本身。
对于一些偶尔的 YAML 用户来说,这些只是要记住的 2 条规则,从而最大限度地减少认知工作。
评论
如果您尝试在 pytest tavern 中转义字符串,避免将字符串解析为 yaml 可能会有所帮助:!raw
some: !raw "{test: 123}"
查看更多信息: https://tavern.readthedocs.io/en/latest/basics.html#type-conversions
这个问题已经有一些很好的答案。 但是,我想扩展它们并提供一些来自新的官方 YAML v1.2.2 规范(2021 年 10 月 1 日发布)的上下文,该规范是考虑 YAML 的所有事物的“真正来源”。
有三种不同的样式可用于表示字符串,每种样式都有自己的(缺点)优点:
YAML 提供三种流标量样式:双引号、单引号和普通(未引号)。每个都提供了可读性和表现力之间的不同权衡。
双引号样式:
- 双引号样式由周围的指示符指定。这是唯一能够使用转义序列来表示任意字符串的样式。这是以必须逃避 and 字符为代价的。
"
\
\
"
单引号样式:
- 单引号样式由周围的指标指定。因此,在单引号标量中,需要重复此类字符。这是在单引号标量中执行的唯一转义形式。特别是,可以自由使用 and 字符。这会将单引号标量限制为可打印字符。此外,只能断开空格字符被非空格包围的长单引号行。
'
\
"
普通(未加引号)样式:
- 普通(未加引号)样式没有识别指标,也不提供任何形式的转义。因此,它是可读性最强、最受限制和最上下文敏感的样式。除了受限制的字符集外,普通标量不得为空或包含前导或尾随空格字符。只能在空格字符被非空格包围的长普通线中断开。 普通标量不得以大多数指标开头,因为这会导致与其他 YAML 构造产生歧义。但是,如果后面跟着一个非空格“安全”字符,则 和 指示符可以用作第一个字符,因为这不会造成歧义。
:
?
-
TL的;博士
话虽如此,根据官方 YAML 规范,应该:
- 在适用的情况下,请使用不带引号的样式,因为它是最易读的。
- 如果字符串内部使用了 and 等字符,请使用单引号样式 (),以避免转义它们,从而提高可读性。
'
"
\
- 当前两个选项不够用时,请使用双引号样式 (),即在需要更复杂的换行符或需要不可打印字符的情况下。
"
评论
string: '012345'
:
和 ' #' 字符组合。此外,在流集合内部或用作隐式键时,纯标量不得包含 [
, ]
、{
、}
和 ,
字符。
&
下面是一个小函数(未针对性能进行优化),如果需要,它会用单引号引用字符串,并测试结果是否可以解组为原始值: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
评论