2009-02-24 130 views
5

我有一个相当大的字符串(〜700k),我需要运行10个正则表达式并计算任何正则表达式的所有匹配。我的快速和肮脏的IMPL是像做re.search(“(表达式1)|(表达式2)| ...”),但我想知道,如果我们想在一个循环,而不是匹配看到任何的性能提升:正则表达式'|'运算符vs每个子表达式的单独运行

换句话说,我想比较的性能:

def CountMatchesInBigstring(bigstring, my_regexes): 
    """Counts how many of the expressions in my_regexes match bigstring.""" 
    count = 0 
    combined_expr = '|'.join(['(%s)' % r for r in my_regexes]) 
    matches = re.search(combined_expr, bigstring) 
    if matches: 
    count += NumMatches(matches) 
    return count 

VS

def CountMatchesInBigstring(bigstring, my_regexes): 
    """Counts how many of the expressions in my_regexes match bigstring.""" 
    count = 0 
    for reg in my_regexes: 
    matches = re.search(reg, bigstring) 
    if matches: 
     count += NumMatches(matches) 
    return count 

我将不再是懒惰和运行一些测试,明天(和后的结果),但我想知道是否答案会跳出来,真正理解正则表达式如何工作的人:)

回答

1

我怀疑正则表达式也会做你正在做的事情......只有更好:)

所以“|”会赢

7

的两件事情会给稍有不同的结果,除非它是保证比赛将匹配一个且只有一个正则表达式。否则,如果匹配2,它将被计数两次。

理论上你的解决方案应该更快(如果表达式是互斥的),因为正则表达式编译器应该能够创建一个更高效的搜索状态机,所以只需要一次传递。我希望这种差异很小,除非表达式非常相似。另外,如果它是一个巨大的字符串(大于700k),则可能会从一次传递中获得收益,因此需要减少n个内存交换(对磁盘或cpu高速缓存)的因数n。

我的赌注是在你的测试中,它不是真的引人注目。我对实际结果感兴趣 - 请张贴结果。

0

我同意amartynov,但我想补充一点,您也可以考虑编译正则表达式第一(re.compile()),ESP。在第二个变体中,那么你可以在循环中保存一些设置时间。也许你也可以在测量时对它进行测量。

我觉得一个镜头性能更好的原因是,我认为它在C空间完全完成,需要解释没有那么多的Python代码。

但期待数字。

+0

因为在他的每一个例子都是正则表达式只使用一次,你不应该获得通过预编译的任何性能改进。即使你使用的每个正则表达式不止一次 – 2009-02-24 09:12:57

+0

,Python的re模块已经缓存编译正则表达式为你,所以在第二次以后它会使用预编译的一个呢。 – nosklo 2009-02-25 14:07:24

0

一个单一的编译和搜索应该产生更快的结果,在表达的较低规模增益是微不足道的,但你越是通过更大的增益运行。把它看作编译一次,匹配vs编译10次并进行匹配。

1

我相信你的第一个实现会更快:

  • 一个Python的性能的关键原则是“移动逻辑C级” - 这意味着内置函数(用C语言编写)的速度更快比纯Python实现。所以,当循环则是由内置的正则表达式模块,它应该是更快
  • 一个正则表达式可以一次搜索多个图纹,这意味着它只有通过你的文件内容,一旦运行,而多个正则表达式会多次读取整个文件。
5

要理解re模块的工作原理 - 在调试模式下编译_sre.c(在_sre.c中将#define VERBOSE置于103行并重新编译python)。在这之后你生病看到这样的内容:

 

>>> import re 
>>> p = re.compile('(a)|(b)|(c)') 
>>> p.search('a'); print '\n\n'; p.search('b') 
|0xb7f9ab10|(nil)|SEARCH 
prefix = (nil) 0 0 
charset = (nil) 
|0xb7f9ab1a|0xb7fb75f4|SEARCH 
|0xb7f9ab1a|0xb7fb75f4|ENTER 
allocating sre_match_context in 0 (32) 
allocate/grow stack 1064 
|0xb7f9ab1c|0xb7fb75f4|BRANCH 
allocating sre_match_context in 32 (32) 
|0xb7f9ab20|0xb7fb75f4|MARK 0 
|0xb7f9ab24|0xb7fb75f4|LITERAL 97 
|0xb7f9ab28|0xb7fb75f5|MARK 1 
|0xb7f9ab2c|0xb7fb75f5|JUMP 20 
|0xb7f9ab56|0xb7fb75f5|SUCCESS 
discard data from 32 (32) 
looking up sre_match_context at 0 
|0xb7f9ab1c|0xb7fb75f4|JUMP_BRANCH 
discard data from 0 (32) 
|0xb7f9ab10|0xb7fb75f5|END 




|0xb7f9ab10|(nil)|SEARCH 
prefix = (nil) 0 0 
charset = (nil) 
|0xb7f9ab1a|0xb7fb7614|SEARCH 
|0xb7f9ab1a|0xb7fb7614|ENTER 
allocating sre_match_context in 0 (32) 
allocate/grow stack 1064 
|0xb7f9ab1c|0xb7fb7614|BRANCH 
allocating sre_match_context in 32 (32) 
|0xb7f9ab20|0xb7fb7614|MARK 0 
|0xb7f9ab24|0xb7fb7614|LITERAL 97 
discard data from 32 (32) 
looking up sre_match_context at 0 
|0xb7f9ab1c|0xb7fb7614|JUMP_BRANCH 
allocating sre_match_context in 32 (32) 
|0xb7f9ab32|0xb7fb7614|MARK 2 
|0xb7f9ab36|0xb7fb7614|LITERAL 98 
|0xb7f9ab3a|0xb7fb7615|MARK 3 
|0xb7f9ab3e|0xb7fb7615|JUMP 11 
|0xb7f9ab56|0xb7fb7615|SUCCESS 
discard data from 32 (32) 
looking up sre_match_context at 0 
|0xb7f9ab2e|0xb7fb7614|JUMP_BRANCH 
discard data from 0 (32) 
|0xb7f9ab10|0xb7fb7615|END 

>>>          
 
0

越少,通过更好的:它只会使用更多的内存,这通常不是一个问题。

如果有什么可以留给解释来处理,它总能找到比一般的人对应一个更快的解决方案(无论是在时间来实施和执行时间)。