在python中解析大文本的最快方法

时间:2016-10-27 12:51:00

标签: python regex

我有一个20MB的100万行文件,格式如下:

# REG A
TextToParse1
TextToParse2
...
...
...
TestToParseX
# reg A
# REG B
TextToParse1
TextToParse2
...
...
...
TestToParseX
# reg B
(continued)

上述格式约20k块。 我使用列表REG Z, REG YYY, REG C, REG ASDSX (order is random)在文件中执行查找。在每次迭代中,我捕获# REG X# reg X之间的相关文本,对其进行处理,然后继续列表中的下一个文本。我正在寻找实现它的最快方法。

我采用了正则表达式方法。我计时了一次,我的测量表明:

start = timer()
pattern = r"(# REG {0})(.*)(# reg {0})".format(reg_name)
match = re.search(pattern, file, re.DOTALL)
end = timer()

是0.2秒。这次20k非常慢。

3 个答案:

答案 0 :(得分:3)

带有.*的模式意味着一些回溯,回溯的数量取决于文本的长度,是否使用了DOTALL修饰符,是否存在匹配。您启用了DOTALL模式,因此,一旦找到# REG A,正则表达式引擎会使用.*抓取整个文本并开始回溯以搜索结束分隔符# reg A。在找到文本之前可能还有很长的路要走。

可以做些什么?如果您的文件格式正确,并且您的块很短(从开始到结束分隔符),则应该足以使用延迟点匹配:

pattern = r"# REG {0}(.*?)# reg {0}".format(reg_name)

这应该仍与re.DOTALL一起使用。

如果块非常长,则懒惰点匹配在性能上会失去展开的模式:

pattern = r'# REG {0}([^#]*(?:#(?! reg {0})[^#]*)*)'

请参阅regex demo

  • # REG {0} - 起始分隔符模式
  • ([^#]*(?:#(?! reg {0})[^#]*)*) - 第1组
    • [^#]* - 零或更多非 - #
    • (?:#(?! reg {0})[^#]*)* - 零个或多个序列
      • #(?! reg {0}) - #字符后跟空格+ reg +空格+名称
      • [^#]* - 零或更多非 - #

这样,我们通过以线性方式使用与尾随分隔符不匹配的块来到达尾随分隔符。

如果分隔符始终位于行的开头,则可以使用相同的技术使用(?m)^# REG {0}(.*(?:\r?\n(?!# reg {0}).*)*)正则表达式。

答案 1 :(得分:2)

你可以这样做:

)

(随意用它制作发电机)

使用itertools:

with open('file.txt') as fh:
    for line in fh:
        if line.startswith('# REG '):
            reg = line.split()[2]
            blocklist = []
            for line in fh:
                if line.startswith('# reg '):
                    # do what you need here
                    # print(reg)
                    # print(block)
                    block = ''.join(blocklist)
                    break
                blocklist.append(line)

使用正则表达式:

from itertools import takewhile

with open('file.txt') as fh:
    for line in fh:
        if line.startswith('# REG '):
            reg = line.split()[2]
            block = ''.join(takewhile(lambda x: not(x.startswith('# reg ')), fh))
            # do what you want here
            # print(reg)
            # print(block)

答案 2 :(得分:1)

如果您只是寻找以# REG# reg开头的行,则根本不需要使用正则表达式。这应该足够了:

def loadmyfile(filename):
    reg = ""
    nlp = 0
    for line in open(filename,"r"):
        if line[:6] == "# REG ":
            reg = line[6:]
        elif line[:6] == "# reg ":
            reg = ""
        else:
            # (Process the data here)
            nlp += 1
    print "Number of lines processed: %d" % nlp