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 的记录,而不是使用条目名称 '>Entry1.1' 直到下一个 '>' 作为 REGEX 中的标记来提取其间的行。但我不知道如何构建如此复杂的 REGEX 表达式。一旦我有了这样的表达,我就会把它放在一个 FOR 循环中:

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

什么可能是正则表达式来执行特定条目的此类记录提取?有没有更多的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”。
我有义务写这个而不是仅仅.+?因为我放置了标志re.DOTALL 并且模式部分.+?将一直运行到条目结束。
但是,我只想验证在输入的引用之后(由模式中的 %s 表示),在行尾之前不会有补充数字,输入错误

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

3)

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

4)

我希望匹配在所需条目的末尾停止,而不是在下一个条目内,以便由括号定义的组(>Entry *%s(?![^\n]+?\d).+?)将准确捕获我们想要
的因此,我在末尾放了一个积极的look-ahaed断言(?=>|(?:\s*\Z))说运行中的不贪婪.+?必须停止匹配的字符是>(下一个条目的开始)或字符串的结尾\Z
由于最后一个条目的结尾可能不完全是整个字符串的结尾,我把\s*它的意思是“最后可能的空格”。
所以\s*\Z意味着“在撞到字符串末尾之前可以有空格”空格是a blank , \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 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 回答