user2050283は間違いなく正しいです、それはyamlであり、これにより解析が簡単になります。主に教育上の理由で、私はそれを自分で解析しようとしました。フィードバックをお待ちしております。
データの構造は階層的で、ツリーのようです。それでは、Pythonでツリーをできるだけ単純に定義しましょう(参照):
from collections import defaultdict
def tree(): return defaultdict(tree)
次に、このツリーを解析関数で使用してみましょう。行を繰り返し、インデントを調べ、それと現在のパス(別名ブレッドクラム)の記録を保持し、行をキーと値(存在する場合)に分割してツリーを埋めようとします。必要に応じて、以下に示す個別の関数として論理チャンクを抽出しました。インデントが以前のインデントと一致しない場合、エラーがスローされます。基本的に、Pythonがソースコードに対して行うのと同じです。
def load_data(f):
doc = tree()
previous_indents = [""]
path = [""]
for line in map(lambda x: x.rstrip("\n"),
filter( is_valid_line, f)
):
line_wo_indent = line.lstrip(" ")
indent = line[:(len(line) - len(line_wo_indent))]
k, v = read_key_and_value(line_wo_indent)
if len(indent) > len(previous_indents[-1]):
previous_indents.append(indent)
path.append(k)
elif len(indent) == len(previous_indents[-1]):
path[-1] = k
else: # indent is shorter
try:
while previous_indents[-1] != indent:
previous_indents.pop()
path.pop()
except IndexError:
raise IndentationError("Indent doesn't match any previous indent.")
path[-1] = k
if v is not None:
set_leaf_value_from_path(doc, path, v)
return doc
私が作成したヘルパー関数は次のとおりです。
- set_leaf_value_from_path:ツリー、パス(キーのリスト)、および値を取ります。再帰を使用してツリーに降下し、パスで定義された葉の値を設定します。
- read_key_and_value:行をキーと値に分割します。最初は「:」です。
- is_valid_line:行が空でないか、番号記号で始まるかどうかを確認するために使用されます
これが完全なスクリプトです
from collections import defaultdict
def tree(): return defaultdict(tree)
def dicts(t):
if isinstance(t, dict):
return {k: dicts(t[k]) for k in t}
else:
return t
def load_data(f):
doc = tree()
previous_indents = [""]
path = [""]
for line in map(lambda x: x.rstrip("\n"),
filter( is_valid_line, f)
):
line_wo_indent = line.lstrip(" ")
indent = line[:(len(line) - len(line_wo_indent))]
k, v = read_key_and_value(line_wo_indent)
if len(indent) > len(previous_indents[-1]):
previous_indents.append(indent)
path.append(k)
elif len(indent) == len(previous_indents[-1]):
path[-1] = k
else: # indent is shorter
try:
while previous_indents[-1] != indent:
previous_indents.pop()
path.pop()
except IndexError:
raise IndentationError("Indent doesn't match any previous indent.")
path[-1] = k
if v is not None:
set_leaf_value_from_path(doc, path, v)
return doc
def set_leaf_value_from_path(tree_, path, value):
if len(path)==1:
tree_[path[0]] = value
else:
set_leaf_value_from_path(tree_[path[0]], path[1:], value)
def read_key_and_value(line):
pos_of_first_column = line.index(":")
k = line[:pos_of_first_column].strip()
v = line[pos_of_first_column+1:].strip()
return k, v if len(v) > 0 else None
def is_valid_line(line):
if line.strip() == "":
return False
if line.lstrip().startswith("#"):
return False
return True
if __name__ == "__main__":
import cStringIO
document_str = """
statistics:
time-started: Tue Feb 5 15:33:35 2013
time-sampled: Thu Feb 7 12:25:39 2013
statistic:
active: 0
interactive: 0
count: 1
up:
packets: 2
bytes: 2
down:
packets: 3
bytes: 3
"""
f = cStringIO.StringIO(document_str)
doc = load_data(f)
from pprint import pprint
pprint(dicts(doc))
既知の制限:
- 値としてサポートされているのはスカラーのみ
- 値としての文字列スカラーのみ
- 複数行のスカラーはサポートされていません
- コメントは、定義のように実装されていません。つまり、コメントは行のどこからでも開始できない場合があります。番号記号で始まる行のみがコメントとして扱われます
これらは既知の制限のみです。YAMLの他の部分もサポートされていないと確信しています。しかし、それはあなたのデータには十分なようです。