4

特定のエントリのデータが必要な巨大なファイルがあります。ファイル構造は次のとおりです。

>Entry1.1
#size=1688
704 1   1   1   4
979 2   2   2   0
1220    1   1   1   4
1309    1   1   1   4
1316    1   1   1   4
1372    1   1   1   4
1374    1   1   1   4
1576    1   1   1   4
>Entry2.1
#size=6251
6110    3   1.5 0   2
6129    2   2   2   2
6136    1   1   1   4
6142    3   3   3   2
6143    4   4   4   1
6150    1   1   1   4
6152    1   1   1   4
>Entry3.2
#size=1777
AND SO ON-----------

私が達成しなければならないことは、特定のエントリのすべての行 (完全なレコード) を抽出する必要があるということです。たとえば、エントリの名前「> Entry1.1」から次の「>」までをREGEXのマーカーとして使用して、その間の行を抽出できるよりも、Entry1.1のレコードが必要です。しかし、そのような複雑な REGEX 式を作成する方法がわかりません。このような式ができたら、FOR ループにします。

For entry in entrylist:
    GET record from big_file
    DO some processing
    WRITE in result file

特定のエントリに対してそのようなレコードの抽出を実行するための REGEX は何でしょうか? これを達成するためのよりpythonicな方法はありますか? これについてあなたの助けをいただければ幸いです。

AK

4

3 に答える 3

4

正規表現で

import re

ss = '''
>Entry1.1
#size=1688
704 1   1   1   4
979 2   2   2   0
1220    1   1   1   4
1309    1   1   1   4
1316    1   1   1   4
1372    1   1   1   4
1374    1   1   1   4
1576    1   1   1   4
>Entry2.1
#size=6251
6110    3   1.5 0   2
6129    2   2   2   2
6136    1   1   1   4
6142    3   3   3   2
6143    4   4   4   1
6150    1   1   1   4
6152    1   1   1   4
>Entry3.2
#size=1777
AND SO ON-----------
'''

patbase = '(>Entry *%s(?![^\n]+?\d).+?)(?=>|(?:\s*\Z))'


while True:
    x = raw_input('What entry do you want ? : ')
    found = re.findall(patbase % x, ss, re.DOTALL)
    if found:
        print 'found ==',found
        for each_entry in found:
            print '\n%s\n' % each_entry
    else:
        print '\n ** There is no such an entry **\n'

の説明'(>Entry *%s(?![^\n]+?\d).+?)(?=>|(?:\s*\Z))':

1)

%sエントリの参照を受け取ります: 1.1 、 2 、 2.1 など

2)

その部分(?![^\n]+?\d)は、検証を行うことです。

(?![^\n]+?\d) は否定的な先読み言明であり、数字の前にある文字を言っては%sいけません。[^\n]+?\d[^\n]+?\d

[^\n]「改行以外の任意の文字」という意味で書きます\n。フラグを立て てパターン部分がエントリの最後まで動作するという理由
だけでなく、これを書く義務があります。 ただし、入力された参照 (パターンでは %s で表される) の後、エラーによって入力された OF THE LINE の末尾の前に補助数字がないことを確認したいだけです。.+?re.DOTALL.+?

これはすべて、 Entry2.1 があり Entry2 がなく、ユーザーが Entry2 が必要で他が必要ないために 2 のみを入力した場合、正規表現は Entry2.1 の存在を検出し、それを生成するためです。実際、Entry2 のように。

3)

の最後で'(>Entry *%s(?![^\n]+?\d).+?)、部分はエントリの完全なブロックをキャッチします 。これ.+?は、ドットが任意の文字を表し、改行を構成するためです。次のパターン部分がエントリの終わり。\n
re.DOTALL.+?

4)

次のエントリ内ではなく、目的のエントリの最後でマッチングを停止して、括弧で定義されたグループが必要 な(>Entry *%s(?![^\n]+?\d).+?)ものを正確にキャッチするようにしたい一致するために実行中の ungreedyが停止しなければならない文字は、 (次のエントリの先頭) または文字列の末尾のいずれかです。 最後のエントリの末尾が文字列全体の末尾ではない可能性があるため、「末尾の前に空白がある可能性がある」という意味にします。 したがって、 「文字列の末尾にぶつかる前に空白がある可能性がある」ことを意味します空白は、、、、、、
(?=>|(?:\s*\Z)).+?>\Z
\s*
\s*\Zblank \f\n\r\t\v

于 2013-02-19T19:56:40.137 に答える
1

私は正規表現が苦手なので、できる限り正規表現以外のソリューションを探すようにしています。Python では、反復ロジックを格納する自然な場所はジェネレーターにあるため、次のようなものを使用します (itertools を必要としないバージョン)。

def group_by_marker(seq, marker):
    group = []
    # advance past negatives at start
    for line in seq:
        if marker(line):
            group = [line]
            break
    for line in seq:
        # found a new group start; yield what we've got
        # and start over
        if marker(line) and group:
            yield group
            group = []
        group.append(line)
    # might have extra bits left..
    if group:
        yield group

あなたの例では、次のようになります。

>>> with open("entry0.dat") as fp:
...     marker = lambda line: line.startswith(">Entry")
...     for group in group_by_marker(fp, marker):
...         print(repr(group[0]), len(group))
...         
'>Entry1.1\n' 10
'>Entry2.1\n' 9
'>Entry3.2\n' 4

このアプローチの利点の 1 つは、複数のグループをメモリに保持する必要がないことです。そのため、非常に大きなファイルの場合に便利です。正規表現ほど高速ではありませんが、ファイルが 1 GB の場合、とにかく I/O バウンドになる可能性があります。

于 2013-02-19T21:14:22.187 に答える
0

あなたが何を求めているのか完全にはわかりません。これで近づきますか?すべてのエントリを辞書キーとそのすべてのエントリのリストとして配置します。私が信じているようにフォーマットされていると仮定します。重複したエントリがありますか? これが私が持っているものです:

entries = {}
key = ''
for entry in open('entries.txt'):
    if entry.startswith('>Entry'):
       key = entry[1:].strip() # removes > and newline
       entries[key] = []
    else:
       entries[key].append(entry)
于 2013-02-19T19:23:32.620 に答える