0

以下のようなテキスト文字列があります。

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: 0
    up:
        packets: 0
        bytes: 0
    down:
        packets: 0
        bytes: 0

上記のような文字列を解析する必要があります (解析する必要がある文字列は、実際にははるかに大きく/深くなります。ここでは例を示しました)。私が考えるいくつかの要素を解析する最も簡単な方法は、この文字列を XML 文字列に変換し、xml.etree.ElementTree探している要素を選択するために使用することです。

したがって、上記の文字列を以下のような XML 文字列に変換したいと思います。

<statistics>
    <time-started>Tue Feb  5 15:33:35 2013</time-started>
    <time-sampled>Thu Feb  7 12:25:39 2013</time-sampled>
    <statistic>
        <active>0</active>
        <interactive>0</interactive>
    </statistic>
    <count>0</count>
    <up>
        <packets>0</packets>
        <bytes>0</bytes>
    </up>
    <down>
        <packets>0</packets>
        <bytes>0</bytes>
    </down>
</statistics>

ご覧のとおり、すべての情報を文字列で使用して XML に変換できます。これを行う簡単な方法またはモジュールがあれば、車輪の再発明はしたくありません。

4

2 に答える 2

2

基本的に、YAML を XML に変換しようとしています。PyYAML を使用して入力文字列を python dict に解析し、xml ジェネレーターを使用して dict を XML に変換できます。

于 2013-02-07T12:51:05.090 に答える
0

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の他の部分もサポートされていないと確信しています。しかし、それはあなたのデータには十分なようです。

于 2013-02-07T14:52:13.177 に答える