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



我有一个巨大的文件,我需要其中特定条目的数据。文件结构为:

>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能为特定条目执行这样的记录提取吗?还有什么比蟒蛇更能做到这一点的方法吗?我很感激你在这方面的帮助。

AK

使用regex

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%sn' % 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,也就是说数字d 之前的任何字符[^n]+?

我把CCD_ 9写为";除了换行符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*放在这里,意思是";最后可能出现的空白"
所以CCD_ 25的意思是";在撞到字符串"的末尾之前可能存在空白;空白是blank fnrtv

我不擅长正则表达式,所以我尽可能寻找非正则表达式的解决方案。在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.1n' 10
'>Entry2.1n' 9
'>Entry3.2n' 4

这种方法的一个优点是,我们永远不需要在内存中保留多个组,因此对于非常大的文件来说,它很方便。它几乎没有regex那么快,尽管如果文件是1GB,那么您可能无论如何都会被I/O绑定。

不完全确定你在问什么。这能拉近你的距离吗?它将把你的所有条目作为字典关键字,并列出所有条目。假设它的格式像我相信的那样。它有重复的条目吗?这是我得到的:

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)

最新更新