优化正则表达式技巧

时间:2015-09-12 16:54:31

标签: python regex parsing optimization

我想知道正则表达式的优化技术

所以我试图解析400k行语料库中的每一个钱。我还需要包含"$10,999.04"以及"one billion and six hundred twenty five thousand dollars"之类的行以及介于两者之间的所有内容。这需要一个非常冗长的正则表达式,包含多个组实例,如

MONEYEXPRESSION = '(?:\d\d?\d?(?:,?\d{3})*(?:\.\d+)?)'
(one|two|...|ninety[\s-]?nine|hundred|a hundred|MONEYEXPRESSION)((\s*and\s*|\s*-\s*|\s*)(one|two|...|ninety[\s-]?nine|hundred|a hundred|MONEYEXPRESSION))*

更重要的是,为了要求它成为一个货币实例并避免匹配"five hundred people were at the event"等行,我有4个OR要求"$", "dollars?", or "cents?"具体的选项句子中的位置至少一次。

正则表达式几乎是20k个字符! :(

你可以想象一个表达这种广泛的表达,任何不好的做法,它确实增加了时间。我一直在语料库上运行这个过去2个小时,但仍然没有完成匹配。我想知道优化和修剪不必要的正则表达式的一些最佳实践是什么。我正在使用的操作是昂贵的,可以补充更好的操作。如果可能有更好的方法来解决这个问题?

3 个答案:

答案 0 :(得分:4)

您一直在询问有关优化效果的问题,因此请注意这一点。正则表达式引擎真正变慢的原因是backtracking,导致回溯的原因是可能在字符串中的不同位置成功的部分,没有明确的方法来决定。所以尝试这些经验法则:

  1. 从上面的回溯链接:"在嵌套重复运算符时,请确保只有一种方法可以匹配相同的匹配。"

  2. 避免使用大型可选组件。而不是像(<number>? <number>)? <number>那样匹配具有空格分隔元素的序列,而是写(<number> ?)+

  3. 避免使用空字符串可以满足的组件 - 引擎会尝试在每个位置满足它们。

  4. 确保regexp中的无约束部分的长度有界,特别是如果后面的部分无法可靠识别。 A.*B?之类的内容正在惹麻烦 - 这可以匹配以A开头的任何

  5. 不要使用超前/外观。几乎总是有一种更简单的方法。

  6. 更一般地说,保持简单。我不确定你是如何设法达到这个任务的20K字符的,但我敢打赌有办法简化它。一个考虑因素是,匹配你不会看到的东西是可以的。

    例如,为什么要匹配一到九十九的所有数字,而不仅仅是它们的组件?是的,你会像九十九美元和#34;那样与废话相提并论,但这并没有坏处。 您正在搜索金额,而不是验证输入。例如,这应该与所有写入的金额少于一百万美元相匹配:

    ((one|two|three|...|twenty|thirty|...|ninety|hundred|thousand|and) ?)+ (dollars?|euros?)\b
    

    由于这是标记的&#34; python&#34;,这里还有两个建议:

    1. 如果任务(或作业)允许您逐步分解搜索,请执行此操作。 执行所有操作的正则表达式通常必须如此复杂,以至于它比简单地按顺序运行多个搜索要慢。

    2. 即使您被限制使用一个怪物正则表达式,也要将其分段编写并使用python将其组合成一个字符串。它在执行时没有任何区别,但使用起来会容易得多。

答案 1 :(得分:0)

我会尝试做某种事情:

keywords = ["$","dollar","dollars","cent","cents"]
my_file = r"c:\file.txt"
output = r"c:\output.txt"
filtered_lines = []
with open(my_file,"r") as f:
     for line in f:
         for k in keywords:
             if k in line:
                filtered_lines.append(line)
                break
with open(output,"w") as o:
    o.write("\n".join(filtered_lines))

答案 2 :(得分:0)

我将数字和写出的正则表达式分开并分两步完成,首先提取数字量(这是容易的部分),然后执行写出的数量。

写出来的部分最有问题的是,如果你有one hundred people,它会尝试所有数十亿,数千以及单词one上已有的所有内容,最后才发现没有dollars。但更糟糕的是,它会再次尝试hundred这个词的所有内容,然后再尝试people ...的所有内容 因此,理想情况下它应该从后面开始,所以它不会尝试将所有内容与每个单词匹配,而只是“美元”,“美分”和“#39;美分”。或任何地方,只有那时才能做昂贵的部分。

因此,如果可能的话,尝试将文件与前面的文件匹配,以便写出来的内容。它肯定会很难解决这个问题,但我敢打赌它会明显加快 如果不可能,我希望你现在至少知道主要瓶颈在哪里。

啊,一些单词边界也可能有助于减少每个字符的每次开头的匹配 ...我没有在上面提到它,但实际上对于这个例子,引擎开始匹配&#39; o&#39;然后又在&#39; n&#39;&#39; e&#39;&#39; &#39;等等。