从文件中加载特定的 PyYAML 文档
Load specific PyYAML documents from file
我有一个 .yml
文件,我正在尝试从中加载某些文档。我知道:
print yaml.load(open('doc_to_open.yml', 'r+'))
将打开 .yml
文件中的第一个(或唯一一个)文档,并且:
for x in yaml.load_all(open('doc_to_open.yml', 'r+')):
print x
这将打印文件中的所有 YAML 文档。但是说我只想打开文件中的前三个文件,或者想打开文件中的第8个文件。我该怎么做?
如果您根本不想解析前七个 YAML 文件,例如出于效率原因,您必须自己搜索第 8 个文档。
有可能挂接到解析器的第一阶段并计算流中 DocumentStartTokens()
的数量,并且仅在 8 日之后开始传递令牌,并在 9 日停止传递,但这样做绝非易事。这至少仍然会标记所有前面的文档。
完全低效的方法是使用 .load_all()
和 select 适当的文档,完成后 tokenizing/parsing/composing/resolving 所有文件 ¹:
import sys
import ruamel.yaml
yaml = ruamel.yaml.YAML()
for idx, data in enumerate(yaml.load_all(open('input.yaml'):
if idx == 7:
yaml.dump(data, sys.stdout)
如果你运行上面的文件input.yaml
:
---
document: 0
---
document: 1
---
document: 2
---
document: 3
---
document: 4
---
document: 5
---
document: 6
---
document: 7 # < the 8th document
---
document: 8
---
document: 9
...
你得到输出:
document: 7 # < the 8th document
不幸的是,您不能天真地计算文档 markers (---
) 的数量,因为文档不必以一个开头:
document: 0
---
document: 1
.
.
如果文件以 directive 开头,也不必在第一行有标记 ²:
%YAML 1.2
---
document: 0
---
document: 1
.
.
或以仅包含评论的 "document" 开头:
# the 8th document is the interesting one
---
document: 0
---
document: 1
.
.
考虑到您可以使用的所有内容:
def get_nth_yaml_doc(stream, doc_nr):
doc_idx = 0
data = []
for line in stream:
if line == u'---\n' or line.startswith('--- '):
doc_idx += 1
continue
if line == '...\n':
break
if doc_nr < doc_idx:
break
if line.startswith(u'%'):
continue
if doc_idx == 0: # no initial '---' YAML files don't start with
if line.lstrip().startswith('#'):
continue
doc_idx = 1
if doc_idx == doc_nr:
data.append(line)
return yaml.load(''.join(data))
with open("input.yaml") as fp:
data = get_nth_yaml_doc(fp, 8)
yaml.dump(data, sys.stdout)
并得到:
document: 7 # < the 8th document
在上述所有情况下,都非常有效,甚至无需对前面的 YAML 文档(或以下)进行标记。
还有一个额外的警告,即 YAML 文件可以以 byte-order-marker, and that the individual documents within a stream 开头,也可以以这些标记开头。上面的例程不处理那个。
¹ 这是使用 ruamel.yaml 完成的,我是作者,它是 PyYAML 的增强版本。 AFAIK PyYAML 的工作方式相同(但例如会删除往返评论)。
² 从技术上讲,该指令在它自己的 directives document 中,因此您应该将其视为文档,但 .load_all()
不会返回该文档,因此我不将其视为这样.
我有一个 .yml
文件,我正在尝试从中加载某些文档。我知道:
print yaml.load(open('doc_to_open.yml', 'r+'))
将打开 .yml
文件中的第一个(或唯一一个)文档,并且:
for x in yaml.load_all(open('doc_to_open.yml', 'r+')):
print x
这将打印文件中的所有 YAML 文档。但是说我只想打开文件中的前三个文件,或者想打开文件中的第8个文件。我该怎么做?
如果您根本不想解析前七个 YAML 文件,例如出于效率原因,您必须自己搜索第 8 个文档。
有可能挂接到解析器的第一阶段并计算流中 DocumentStartTokens()
的数量,并且仅在 8 日之后开始传递令牌,并在 9 日停止传递,但这样做绝非易事。这至少仍然会标记所有前面的文档。
完全低效的方法是使用 .load_all()
和 select 适当的文档,完成后 tokenizing/parsing/composing/resolving 所有文件 ¹:
import sys
import ruamel.yaml
yaml = ruamel.yaml.YAML()
for idx, data in enumerate(yaml.load_all(open('input.yaml'):
if idx == 7:
yaml.dump(data, sys.stdout)
如果你运行上面的文件input.yaml
:
---
document: 0
---
document: 1
---
document: 2
---
document: 3
---
document: 4
---
document: 5
---
document: 6
---
document: 7 # < the 8th document
---
document: 8
---
document: 9
...
你得到输出:
document: 7 # < the 8th document
不幸的是,您不能天真地计算文档 markers (---
) 的数量,因为文档不必以一个开头:
document: 0
---
document: 1
.
.
如果文件以 directive 开头,也不必在第一行有标记 ²:
%YAML 1.2
---
document: 0
---
document: 1
.
.
或以仅包含评论的 "document" 开头:
# the 8th document is the interesting one
---
document: 0
---
document: 1
.
.
考虑到您可以使用的所有内容:
def get_nth_yaml_doc(stream, doc_nr):
doc_idx = 0
data = []
for line in stream:
if line == u'---\n' or line.startswith('--- '):
doc_idx += 1
continue
if line == '...\n':
break
if doc_nr < doc_idx:
break
if line.startswith(u'%'):
continue
if doc_idx == 0: # no initial '---' YAML files don't start with
if line.lstrip().startswith('#'):
continue
doc_idx = 1
if doc_idx == doc_nr:
data.append(line)
return yaml.load(''.join(data))
with open("input.yaml") as fp:
data = get_nth_yaml_doc(fp, 8)
yaml.dump(data, sys.stdout)
并得到:
document: 7 # < the 8th document
在上述所有情况下,都非常有效,甚至无需对前面的 YAML 文档(或以下)进行标记。
还有一个额外的警告,即 YAML 文件可以以 byte-order-marker, and that the individual documents within a stream 开头,也可以以这些标记开头。上面的例程不处理那个。
¹ 这是使用 ruamel.yaml 完成的,我是作者,它是 PyYAML 的增强版本。 AFAIK PyYAML 的工作方式相同(但例如会删除往返评论)。
² 从技术上讲,该指令在它自己的 directives document 中,因此您应该将其视为文档,但 .load_all()
不会返回该文档,因此我不将其视为这样.