有没有办法在python中真正挑选编译的正则表达式?

Ada*_*dam 18 python regex pickle

我有一个包含300多个正则表达式的python控制台应用程序.每个版本都修复了正则表达式集.当用户运行应用程序时,整个正则表达式集将应用于从一次(非常短的工作)到数千次(长时间工作)的任何地方.

我想通过预先编译正则表达式,将编译的正则表达式pickle到文件,然后在运行应用程序时加载该文件来加快较短的作业.

python re模块是高效的,正则表达式编译开销对于长作业来说是完全可以接受的.然而,对于短期工作,它占整个运行时间的很大一部分.一些用户希望运行许多小作业以适应他们现有的工作流程.编译正则表达式大约需要80毫秒.除正则表达式编译外,短作业可能需要20ms-100ms.因此,对于短期工作,开销可以是100%或更多.这是在Windows和Linux下的Python27.

必须使用DOTALL标志应用正则表达式,因此需要在使用前进行编译.在这种情况下,大型编译缓存显然无济于事.正如一些人已经指出的那样,序列化编译的正则表达式的默认方法实际上并没有做太多.

re和sre模块将模式编译成一个小的自定义语言,具有自己的操作码和一些辅助数据结构(例如,用于表达式中使用的字符集).re.py中的pickle函数可以轻松实现.它是:

def _pickle(p):
    return _compile, (p.pattern, p.flags)

copy_reg.pickle(_pattern_type, _pickle, _compile)
Run Code Online (Sandbox Code Playgroud)

我认为这个问题的一个很好的解决方案是更新re.py中_pickle的定义,它实际上是对已编译的模式对象进行了腌制.不幸的是,这超出了我的python技能.不过,我打赌,这里有人知道该怎么做.

我意识到我不是第一个提出这个问题的人 - 但也许你可以成为第一个对它做出准确而有用的回应的人!

非常感谢您的建议.

Ned*_*der 11

好吧,这不是很好,但它可能是你想要的.我查看了Python 2.6中的sre_compile.py模块,并将其删除了一半,将其切成两半,并使用这两个部分来挑选和解开编译的正则表达式:

import re, sre_compile, sre_parse, _sre
import cPickle as pickle

# the first half of sre_compile.compile    
def raw_compile(p, flags=0):
    # internal: convert pattern list to internal format

    if sre_compile.isstring(p):
        pattern = p
        p = sre_parse.parse(p, flags)
    else:
        pattern = None

    code = sre_compile._code(p, flags)

    return p, code

# the second half of sre_compile.compile
def build_compiled(pattern, p, flags, code):
    # print code

    # XXX: <fl> get rid of this limitation!
    if p.pattern.groups > 100:
        raise AssertionError(
            "sorry, but this version only supports 100 named groups"
            )

    # map in either direction
    groupindex = p.pattern.groupdict
    indexgroup = [None] * p.pattern.groups
    for k, i in groupindex.items():
        indexgroup[i] = k

    return _sre.compile(
        pattern, flags | p.pattern.flags, code,
        p.pattern.groups-1,
        groupindex, indexgroup
        )

def pickle_regexes(regexes):
    picklable = []
    for r in regexes:
        p, code = raw_compile(r, re.DOTALL)
        picklable.append((r, p, code))
    return pickle.dumps(picklable)

def unpickle_regexes(pkl):
    regexes = []
    for r, p, code in pickle.loads(pkl):
        regexes.append(build_compiled(r, p, re.DOTALL, code))
    return regexes

regexes = [
    r"^$",
    r"a*b+c*d+e*f+",
    ]

pkl = pickle_regexes(regexes)
print pkl
print unpickle_regexes(pkl)
Run Code Online (Sandbox Code Playgroud)

我真的不知道这是否有效,或者是否加快了速度.我知道它在我尝试时会打印一份正则表达式列表.它可能对2.6版本非常具体,我也不知道.


Ned*_*der 9

正如其他人所提到的,你可以简单地挑选编译的正则表达式.他们会腌制和去腌制得很好,并且可以使用.但是,它看起来并不像pickle实际上包含编译结果.我怀疑当你使用unpickling的结果时,你会再次产生编译开销.

>>> p.dumps(re.compile("a*b+c*"))
"cre\n_compile\np1\n(S'a*b+c*'\np2\nI0\ntRp3\n."
>>> p.dumps(re.compile("a*b+c*x+y*"))
"cre\n_compile\np1\n(S'a*b+c*x+y*'\np2\nI0\ntRp3\n."
Run Code Online (Sandbox Code Playgroud)

在这两个测试中,您可以看到两个泡菜之间的唯一区别在于字符串.显然编译的正则表达式不会腌制编译的位,只需要再次编译它所需的字符串.

但我总体上想知道你的应用程序:编译一个正则表达式是一个快速的操作,编译正则表达式的工作有多短暂?一种可能性是你正在编译所有300个正则表达式,然后只使用一个用于短期工作.在这种情况下,不要预先编译它们.re模块非常擅长使用已编译正则表达式的缓存副本,因此您通常不必自己编译它们,只需使用字符串形式.re模块将在编译的正则表达式的字典中查找字符串,因此自己抓取编译的表单只会节省字典查找.我可能完全偏离基础,对不起,如果是这样的话.