如果您不想分析前七個YAML文件,例如出於效率原因,您必須自己搜索第8個文檔。
有可能掛鉤到解析器的第一階段,並計算流內的DocumentStartTokens()
的數量,並且只在8號之後開始傳遞令牌,並在9號停止這樣做,但這樣做是遠非瑣碎。而且這至少可以標記所有前面的文件。
完全無效的方式,對於一個有效的替代品IMO來說,需要表現相同的方法是使用.load_all()
並在完成標記/解析/編寫/解析所有文檔後選擇合適的文檔¹:
import ruamel.yaml as yaml
for idx, data in enumerate(yaml.load_all(open('input.yaml', Loader=yaml.RoundTripLoader)):
if idx == 7:
print(yaml.dump(data, Dumper=yaml.RoundTripDumper))
如果您對文檔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
.
.
或「文件」開始由只評論:
# 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), Loader=yaml.RoundTripLoader)
with open("input.yaml") as fp:
data = get_nth_yaml_doc(fp, 8)
print(yaml.dump(data, Dumper=yaml.RoundTripDumper, allow_unicode=True))
,並得到:
document: 7 # < the 8th document
在上述所有情況下,有效地,甚至沒有令牌化前面YAML文件(也不以下)。
還有一個額外的警告,YAML文件可能以byte-order-marker開頭,並且individual documents within a stream可以以這些標記開頭。上面的例程不處理這個。
¹這是使用ruamel.yaml其中我的作家完成,這是PyYAML的增強版本。 AFAIK PyYAML的工作原理是相同的(但是會在例子中刪除註釋)。
²技術上這個指令是在它自己的directives document中,所以你應該把它當作文檔,但是.load_all()
不會給你那個文檔,所以我不這麼認爲。
不需要'load()'或'load_all()'寫一個可寫入的流,而且你並沒有自己寫入已打開的流,所以你應該用'r'替換r +。並且'load()'確實打開了「first ** and only only」文檔,如果存在指示第二個文檔的尾隨文檔分隔標記,它實際上會出錯。 – Anthon