2011-02-04 13 views
12

非常に大きな文字列の中で多数のサブストリングを置き換える最速の方法を探しています。私が使った2つの例を以下に示します。検索と置換のための最速のPythonメソッド

findall()はよりシンプルで洗練されていますが、時間がかかります。

finditer()は大きなファイルを飛ばしますが、これが正しい方法であるかどうかはわかりません。

ここにいくつかのサンプルコードがあります。私が興味を持っている実際のテキストは、サイズが約10MBの単一文字列であり、これらの2つの方法には大きな違いがあることに注意してください。試験に

import re 

def findall_replace(text, reg, rep): 
    for match in reg.findall(text): 
     output = text.replace(match, rep) 
    return output 

def finditer_replace(text, reg, rep): 
    cursor_pos = 0 
    output = '' 
    for match in reg.finditer(text): 
     output += "".join([text[cursor_pos:match.start(1)], rep]) 
     cursor_pos = match.end(1) 
    output += "".join([text[cursor_pos:]]) 
    return output 

reg = re.compile(r'(dog)') 
rep = 'cat' 
text = 'dog cat dog cat dog cat' 

finditer_replace(text, reg, rep) 

findall_replace(text, reg, rep) 

UPDATE追加しましre.sub方法:

def sub_replace(reg, rep, text): 
    output = re.sub(reg, rep, text) 
    return output 

結果

re.sub() - 0:00:00.031000
finditer() - 0 :00:00.109000
findall() - 0:01:17.260000

+0

を2つ目は本当にはるかに高速ですか?私には不思議そうだ、彼らは約を取るべきである。同じ時間。そして私は両方の方法が正しいと思います。 –

+0

なぜreのサブメソッドを使っていないのですか? –

+1

+ =を文字列とするのは、リストを作成して ""を使って結合するO(n)と比較して、O(n^2)の操作です。 –

答えて

14

標準的な方法は、組み込みの

re.sub(reg, rep, text) 

を使用することですところで、あなたのバージョン間の性能差の理由は、あなたの最初のバージョンでは、各交換が再コピーされる文字列全体を引き起こすことがあります。コピーは高速ですが、10 MBを一度にコピーすると、十分なコピーが遅くなります。

+0

ありがとうございます。私はreall()を使用しませんでした。なぜなら、私が同じことをfindallと同じだと思ったからです。私は再び私のテストを実行し、re.subは明らかに最も速い方法です。結果は質問に追加されました。 – cyrus

4

することができます、そしてそれは確かに最適化された機能ですので、私はあなたが必要だと思い、あなたのfindall_replace()機能が長い理由は、それが各マッチである

re.sub(pattern, repl, string[, count, flags]) 

を使用し、新しい文字列オブジェクトこのコードでは、私はtext = text.replace(match, rep)output = text.replace(match, rep)を置き換えること

ch = '''qskfg qmohb561687ipuygvnjoihi2576871987uuiazpoieiohoihnoipoioh 
opuihbavarfgvipauhbi277auhpuitchpanbiuhbvtaoi541987ujptoihbepoihvpoezi 
abtvar473727tta aat tvatbvatzeouithvbop772iezubiuvpzhbepuv454524522ueh''' 

import re 

def findall_replace(text, reg, rep): 
    for match in reg.findall(text): 
     text = text.replace(match, rep) 
     print id(text) 
    return text 

pat = re.compile('\d+') 
rep = 'AAAAAAA' 

print id(ch) 
print 
print findall_replace(ch, pat, rep) 

注、そうでない場合にのみ、最後の出現が交換される:あなたが実行され、次のコードで見るように、作成されます。

findall_replace()と同じ理由から、finditer_replace()は長い:文字列オブジェクトの繰り返し作成。しかし、前者は反復子re.finditer()を使用しますが、後者はリスト・オブジェクトを作成するため、より長いです。これがイテレータとイテレータの違いです。ところで

1

は、findall_replace()とあなたのコードが安全ではない、それはunawaited結果を返すことができます。

ch = 'sea sun ABC-ABC-DEF bling ranch micABC-DEF fish' 

import re 

def findall_replace(text, reg, rep): 
    for gr in reg.findall(text): 
     text = text.replace(gr, rep) 
     print 'group==',gr 
     print 'text==',text 
    return '\nresult is : '+text 

pat = re.compile('ABC-DE') 
rep = 'DEFINITION' 

print 'ch==',ch 
print 
print findall_replace(ch, pat, rep) 

表示

ch== sea sun ABC-ABC-DEF bling ranch micABC-DEF fish 

group== ABC-DE 
text== sea sun ABC-DEFINITIONF bling ranch micDEFINITIONF fish 
group== ABC-DE 
text== sea sun DEFINITIONFINITIONF bling ranch micDEFINITIONF fish 

result is : sea sun DEFINITIONFINITIONF bling ranch micDEFINITIONF fish 
関連する問題