我以犯罪效率低下的方式使用Python正则表达式

时间:2008-09-28 20:03:14

标签: regex algorithm optimization python

我的目标是创建一个非常简单的模板语言。目前,我正在尝试使用值替换变量,如下所示:

此输入:

  网络

应该产生这个输出:

  

网络这是一个测试变量

我已经开始工作了。但是看看我的代码,我在相同的字符串上运行多个相同的正则表达式 - 这只是冒犯了我的效率感。必须有更好,更Pythonic的方式。 (这是真正冒犯的两个“while”循环。)

这确实通过了单元测试,所以如果这是愚蠢的过早优化,请告诉我 - 我愿意放手。文档中可能有许多这些变量定义和用法,但不是数百个。但我怀疑(对其他人)显而易见的是改善这一点的方法,我很好奇StackOverflow人群会想出什么。

def stripMatchedQuotes(item):
    MatchedSingleQuotes = re.compile(r"'(.*)'", re.LOCALE)
    MatchedDoubleQuotes = re.compile(r'"(.*)"', re.LOCALE)
    item = MatchedSingleQuotes.sub(r'\1', item, 1)
    item = MatchedDoubleQuotes.sub(r'\1', item, 1)
    return item




def processVariables(item):
    VariableDefinition = re.compile(r'<%(.*?)=(.*?)%>', re.LOCALE)
    VariableUse = re.compile(r'<%(.*?)%>', re.LOCALE)
    Variables={}

    while VariableDefinition.search(item):
        VarName, VarDef = VariableDefinition.search(item).groups()
        VarName = stripMatchedQuotes(VarName).upper().strip()
        VarDef = stripMatchedQuotes(VarDef.strip())
        Variables[VarName] = VarDef
        item = VariableDefinition.sub('', item, 1)

    while VariableUse.search(item):
        VarName = stripMatchedQuotes(VariableUse.search(item).group(1).upper()).strip()
        item = VariableUse.sub(Variables[VarName], item, 1)

    return item

10 个答案:

答案 0 :(得分:10)

可能改进的第一件事是将re.compile移到函数之外。编译是缓存的,但检查它是否有速度命中,以查看它是否已编译。

另一种可能性是使用单个正则表达式如下:

MatchedQuotes = re.compile(r"(['\"])(.*)\1", re.LOCALE)
item = MatchedQuotes.sub(r'\2', item, 1)

最后,您可以将其组合到processVariables中的正则表达式中。考虑Torsten Marek's建议使用re.sub函数,这可以显着改善和简化事物。

VariableDefinition = re.compile(r'<%(["\']?)(.*?)\1=(["\']?)(.*?)\3%>', re.LOCALE)
VarRepl = re.compile(r'<%(["\']?)(.*?)\1%>', re.LOCALE)

def processVariables(item):
    vars = {}
    def findVars(m):
        vars[m.group(2).upper()] = m.group(4)
        return ""

    item = VariableDefinition.sub(findVars, item)
    return VarRepl.sub(lambda m: vars[m.group(2).upper()], item)

print processVariables('<%"TITLE"="This Is A Test Variable"%>The Web <%"TITLE"%>')

以下是100000次跑步的时间安排:

Original       : 13.637
Global regexes : 12.771
Single regex   :  9.095
Final version  :  1.846

[编辑]添加缺少的非贪婪说明符

[Edit2]添加了.upper()调用,因此不区分大小写,如原始版本

答案 1 :(得分:3)

sub可以使用可调用的参数而不是简单的字符串。使用它,您可以使用一个函数调用替换所有变量:

>>> import re
>>> var_matcher = re.compile(r'<%(.*?)%>', re.LOCALE)
>>> string = '<%"TITLE"%> <%"SHMITLE"%>'
>>> values = {'"TITLE"': "I am a title.", '"SHMITLE"': "And I am a shmitle."}
>>> var_matcher.sub(lambda m: vars[m.group(1)], string)
'I am a title. And I am a shmitle.

按照eduffy.myopenid.com的建议并保留已编译的正则表达式。

同一个配方可以应用于第一个循环,只需要先存储变量的值,并始终返回""作为替换。

答案 2 :(得分:2)

永远不要创建自己的编程语言。永远。 (我曾经对此规则有例外,但不再有。)

您可以使用现有的语言,更好地满足您的需求。如果您详细说明了您的用例,人们可能会帮助您选择合适的语言。

答案 3 :(得分:2)

创建模板语言一切都很好,但是模板语言的目标之一不应该是易读性和高效解析吗?你给出的例子似乎都不是。

正如Jamie Zawinsky所说:

  有些人在面对的时候   问题,想想“我知道,我会用   正则表达式!“现在他们有了   两个问题。

如果正则表达式是您创建的问题的解决方案,那么最好的选择不是编写更好的正则表达式,而是重新设计您的方法以完全消除它们的使用。正则表达式复杂,昂贵,难以维护,并且(理想情况下)仅应用于解决其他人创建的问题。

答案 4 :(得分:1)

您可以使用r"(\"|')(.*?)\1"一次匹配两种报价 - \1指的是第一组,因此它只匹配匹配的报价。

答案 5 :(得分:1)

你正在调用re.compile。这些全局变量在这里不会受到伤害。

答案 6 :(得分:1)

如果正则表达式只包含一个。*通配符和文字,那么您可以使用find和rfind来定位开始和结束分隔符。

如果它只包含一系列。*?通配符和文字,然后你可以使用一系列的find来完成这项工作。

如果代码对时间要求严格,那么完全脱离正则表达式可能会提高速度。

此外,我认为这是一个LL-parsable language。你可以找一个可以解析这些东西的库。您还可以使用递归调用来执行一次性解析 - 例如,您可以实现processVariables函数以仅消耗第一个引用,然后调用引用匹配函数以消耗下一个引用等。

答案 7 :(得分:1)

为什么不使用Mako?认真。 Mako没有你需要什么功能?也许你可以适应或扩展已经有效的东西。

答案 8 :(得分:0)

不要连续两次调用搜索(在循环条件中,以及循环中的第一个语句)。在循环之前调用(并缓存结果)一次,然后在循环的最后一个语句中调用。

答案 9 :(得分:-3)

为什么不使用XML和XSLT而不是创建自己的模板语言?在XSLT中,您想要做的事情非常简单。