在列表中找到re.compile匹配的最快方法

时间:2013-12-03 14:38:55

标签: python performance list python-2.5

我有一个包含120000行的文本文件,其中每一行都具有以下格式: ean_code; plu; name; price; state

我尝试了各种操作,包括直接处理文件,如果文件刚刚与readlines()一起逐行加载到内存中并且写入列表(在程序开头),则会给出最佳结果。

所以我有这两行:

matcher = re.compile('^(?:'+eanic.strip()+'(?:;|$)|[^;]*;'+eanic.strip()+'(?:;|$))').match
line=[next(l.split(';') for l in list if matcher(l))]
do sth with line....

这些线路试图完成的是,他们试图找到(尽可能快)一个plu / ean,这是由用户输入字段给出的: ean_code或plu

我特别感兴趣的是第二行,因为它会影响我在WinCE设备上的性能(python 2.5的PyCE端口)。

我尝试了所有可能的解决方案,以使其更快,但这是迭代某个列表以找到re.compile正在生成的匹配的最快方法。

除了列表理解中 以外的任何更快的方式来迭代大列表(在我的情况下为120000行)?

我正在寻找任何类型的数据结构(在Python 2.5之前都支持)的方式,它会比上面两行更快地得到结果......

请注意,这是在手持设备(630MHz ARM)上执行的,具有256MB RAM,除了USB之外没有任何连接。遗憾的是,数据库访问和存在不是一种选择。

2 个答案:

答案 0 :(得分:3)

我制作了一个测试文件并测试了一些变体。通过迭代文件来搜索静态字符串(就像您似乎正在做的那样)的最快方法是使用string in line

但是,如果您将使用加载的数据进行多次搜索(根据下面的测试数据实际超过30次),那么为您的(计算)时间生成PLU和EAN的查找表是值得的。 dicts的形式,并将其用于将来的搜索。

loaded 120000 lines
question regex    0.114868402481
simpler regex     0.417045307159
other regex       0.386662817001
startswith        0.236350297928
string in         0.020356798172  <-- iteration winner
dict construction 0.611148500443
dict lookup       0.000002503395  <-- best if you are doing many lookups

测试代码如下:

import re
import timeit

def timefunc(function, times, *args):
    def wrap():
        function(*args)
    t = timeit.Timer(wrap)
    return t.timeit(times) / times

def question(lines):
    eanic = "D41RP9"
    matcher = re.compile('^(?:'+eanic.strip()+'(?:;|$)|[^;]*;'+eanic.strip()+'(?:;|$))').match
    line=[next(l.split(';') for l in lines if matcher(l))]
    return line

def splitstart(lines):
    eanic = "D41RP9"
    ret = []
    for l in lines:
        s = l.split(';')
        if s[0].startswith(eanic) or s[1].startswith(eanic):
            ret.append(l)
    return ret

def simpler(lines):
    eanic = "D41RP9"
    matcher = re.compile('(^|;)' + eanic)
    return [l for l in lines if matcher.search(l)]

def better(lines):
    eanic = "D41RP9"
    matcher = re.compile('^(?:' + eanic + '|[^;]*;' + eanic + ')')
    return [l for l in lines if matcher.match(l)]

def strin(lines):
    eanic = "D41RP9"
    return [l for l in lines if eanic in l]

def mkdicts(lines):
    ean = {}
    plu = {}
    for l in lines:
        s = l.split(';')
        ean[s[0]] = s
        plu[s[1]] = s
    return (ean, plu)

def searchdicts(ean, plu):
    eanic = "D41RP9"
    return (ean.get(eanic, None), plu.get(eanic, None))

with open('test.txt', 'r') as f:
    lines = f.readlines()

    print "loaded", len(lines), "lines"
    print "question regex\t", timefunc(question, 10, lines)
    print "simpler regex\t", timefunc(simpler, 10, lines)
    print "other regex\t", timefunc(simpler, 10, lines)
    print "startswith\t", timefunc(splitstart, 10, lines)
    print "string in\t", timefunc(strin, 10, lines)
    print "dict construction\t", timefunc(mkdicts, 10, lines)
    ean, plu = mkdicts(lines)
    print "dict lookup\t", timefunc(searchdicts, 10, ean, plu)

答案 1 :(得分:0)

起初我查找了一些可用于python 2.5的模块:

您可以使用csv-module来阅读您的数据。可能会更快。

您可以通过pickle or cPickle module存储数据。所以你可以存储python-objects(比如dict,tuples,int等)。比较整数比在字符串中搜索更快。

您遍历列表,但您说您的数据位于文本文件中。不要将整个文本文件加载到列表中。也许以下内容足够快,不需要使用我上面提到的模块。

f = open('source.txt','r') # note: python 2.5, no with-statement yet
stripped_eanic = eanic.strip()
for line in f:
    if stripped_eanic in line: # the IDs have a maximum length, don't they? So maybe just search in line[:20]
        # run further tests, if you think it is necessary
        if further_tests:
            print line
            break
else:
    print "No match"

修改

我想到了上面提到的内容:不要将整个文件加载到列表中。我认为只有在您的搜索是一次性过程并且您的脚本退出时才会出现这种情况。但是如果你想多次搜索,我建议使用dict(如建议使用beerbajay)和cPickle - 文件而不是文本文件。