在两个符号之间提取多行数据 - Regex和Python3

时间:2013-02-19 19:14:43

标签: python regex

我有一个巨大的文件,我需要从中获取特定条目的数据。文件结构是:

>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-----------

我必须要实现的是我需要提取某些条目的所有行(完整记录)。对于e.x.我需要Entry1.1的记录,而不是我可以使用条目名称'> Entry1.1'直到下一个'>'作为REGEX中的标记来提取其间的线条。但我不知道如何构建这样复杂的REGEX表达式。一旦我有了这样的表达式,我就会把它换成FOR循环:

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

对于特定条目,可以执行此类记录提取的REGEX是什么?有没有更多的pythonic方法来实现这一目标?我很感激你的帮助。

AK

3 个答案:

答案 0 :(得分: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 1}}

我写[^\n]表示“换行符\n以外的任何字符” 我不得不写这个而不仅仅是.+?,因为我放置了标志re.DOTALL,模式部分.+?将一直行动直到条目结束。
但是,我只想验证输入的参考(在模式中由%s表示)后,在错误输入的行结束之前不会有补充数字

所有这一切都是因为如果有一个Entry2.1但没有Entry2,并且用户只输入2因为他想要Entry2而没有其他,那么正则表达式会检测到Entry2.1的存在并且会产生它,尽管用户实际上非常喜欢Entry2。

3)

'(>Entry *%s(?![^\n]+?\d).+?)结束时,部分.+?将捕获条目的完整块,因为点代表任何字符,包含换行符\n
为了达到这个目的,我放置了标志re.DOTALL,以便使以下模式部分.+?能够传递换行符直到条目结束。

4)

我希望匹配在所需条目的末尾停止,而不是在下一个条目的内部停止,以便由(>Entry *%s(?![^\n]+?\d).+?)中的括号内定义的组将准确捕获我们想要的内容。 因此,我在最后给出了一个积极的外观断言(?=>|(?:\s*\Z)),它表示运行的不合格.+?必须停止匹配的字符是>(下一个条目的开头) )或字符串\Z的结尾 由于最后一个Entry的结尾可能不完全是整个字符串的结尾,我将\s*表示“最终可能的空格”。 所以\s*\Z意味着“在碰到字符串的末尾之前可能有空格” 空白是blank\f\n\r\t\v

答案 1 :(得分:1)

我对正则表达式不好,所以我尽力寻找非正则表达式解决方案。在Python中,存储迭代逻辑的自然位置在生成器中,因此我使用类似的东西(no-itertools-required version):

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 GB,你可能无论如何都是I / O绑定。

答案 2 :(得分: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)