2009-02-24 122 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

越少,通過更好的:它只會使用更多的內存,這通常不是一個問題。

如果有什麼可以留給解釋來處理,它總能找到比一般的人對應一個更快的解決方案(無論是在時間來實施和執行時間)。