2

次のようなファイルがあります。

useless stuff

fruit: apple
fruit: banana

useless stuff

fruit: kiwi
fruit: orange
fruit: pear

useless stuff

アイデアは、すべての果物の名前を、表示される順序で、グループ別にキャッチすることです。上記の例では、出力は次のようになります。

[['apple', 'banana'], ['kiwi', 'orange', 'pear']]

複数行の regexp のすべての一致を繰り返し、'^fruit: (.+)$'果物の名前が見つかった行が互いに続いているように見える場合は、同じリストに果物の名前を追加することで、これを行うことに成功しました。

ただし、これは果物の名前の置換を行うには実用的ではありません (マッチの開始インデックスと終了インデックスを追跡することが必須になります)。

私はこれを試しました:

re.findall(r'(?:^fruit: (.+)$\n)+', thetext, re.M)

ただし、1行しか返されません。

どこが間違っていますか?

4

6 に答える 6

1

通常、グループは最新の一致のみをキャプチャするため、正規表現でこの方法で「グループ化」することはできません。回避策は、グループを文字通り繰り返すことです。

matches = re.findall(r'(?m)(?:^fruit: (.+)\n)(?:^fruit: (.+)\n)?(?:^fruit: (.+)\n)?', text)
# [('apple', 'banana', ''), ('kiwi', 'orange', 'pear')]

これがタスクに適している場合 (たとえば、5 ~ 6 グループ以下)、そのような式をその場で簡単に生成できます。そうでない場合、唯一のオプションは 2 パス マッチです (これは既にお持ちのものと似ていると思います)。

matches = [re.findall(': (.+)', x) 
    for x in re.findall(r'(?m)((?:^fruit: .+\n)+)', text)]
# [['apple', 'banana'], ['kiwi', 'orange', 'pear']]

非標準の (まだ)正規表現モジュールは、「キャプチャ」と呼ばれる興味深い方法を提供します。m.captures(n)次のように、最新のものだけでなく、グループのすべての一致を返しますm.group(n)

import regex
matches = [x.captures(2) for x in regex.finditer(r'(?m)((?:^fruit: (.+)\n)+)', text)]
# [['apple', 'banana'], ['kiwi', 'orange', 'pear']]
于 2013-04-13T11:01:51.180 に答える
1

これにより、後でより複雑な式が必要になる可能性があると述べたように、正規表現を保持できます。

>>> import re
>>> from itertools import groupby
>>> with open('test.txt') as fin:
        groups = groupby((re.match(r'(?:fruit: )(.+)', line) for line in fin),
                         key=bool) # groups based on whether each line matched
        print [[m.group(1) for m in g] for k, g in groups if k]
        # prints each matching group


[['apple', 'banana'], ['kiwi', 'orange', 'pear']]

正規表現なし:

>>> with open('test.txt') as f:
        print [[x.split()[1] for x in g]
               for k, g in groupby(f, key=lambda s: s.startswith('fruit'))
               if k]


[['apple', 'banana'], ['kiwi', 'orange', 'pear']]
于 2013-04-13T10:26:09.333 に答える
1

別の方法:

import re
with open('input') as file:
    lines = "".join(file.readlines())
    fruits = [[]]
    for fruit in re.findall(r'(?:fruit: ([^\n]*))|(?:\n\n)', lines, re.S):
        if fruit == '': 
            if len(fruits[-1]) > 0:
                fruits.append([])
        else:
            fruits[-1].append(fruit)
    del fruits[-1]
    print fruits

出力

[['apple', 'banana'], ['kiwi', 'orange', 'pear']]
于 2013-04-13T10:55:35.520 に答える
0

絶対に必要でない限り、私は正規表現を使うのが好きではありません。一歩後退してあなたのケースを見て、私の最初の傾向は、入力ファイルをPythonに供給する前に、awkのような特殊なツールを使用してcsvのようなものに実際にマッサージするべきではないかどうかを考えることです.

そうは言っても、もちろん、明確な正規表現のないPythonを使用して、やりたいことを達成することはできます。例(透明性を犠牲にすることなく削減できると確信しています):

# newlst keeps track of whether you should start a new sublist
newlst=False
# result is the end result list of lists
result = []
# lst is the sublist which gets reset every time a grouping concludes
lst = []

with open('input.txt') as f:
    for line in f.readlines():
        # is the first token NOT a fruit?
        if line.split(':')[0] != 'fruit':
            # if so, start a new sublist
            newlst=True
            # just so we don't append needless empty sublists
            if len(lst) > 0: result.append(lst)
            # initialise a new sublist, since last line wasn't a fruit and
            # this implies a new group is starting
            lst = []
        else:
            # first token IS a fruit. So append it to the sublist
            lst.append(line.split()[1])

print result
于 2013-04-13T10:33:36.050 に答える
0

どうですか:

re.findall(r'fruit: ([\w]+)\n|[^\n]*\n', str, re.M);

結果:

['', '', 'apple', 'banana', '', '', '', 'kiwi', 'orange', 'pear', '']

これは [['apple', 'banana'], ['kiwi', 'orange', 'pear']] に簡単に変換できます

アイデアの例

于 2013-04-13T10:49:33.057 に答える