用于搜索和替换大字符串的最快Python方法

用于搜索和替换大字符串的最快Python方法,python,regex,Python,Regex,我正在寻找最快的方法来替换一个非常大的字符串中的大量子字符串。这里有两个我用过的例子 findall()感觉更简单、更优雅,但它需要惊人的时间 finditer()可以浏览一个大文件,但我不确定这样做是否正确 下面是一些示例代码。请注意,我感兴趣的实际文本是一个大小约为10MB的字符串,这两种方法之间存在巨大差异 import re def findall_replace(text, reg, rep): for match in reg.findall(text):

我正在寻找最快的方法来替换一个非常大的字符串中的大量子字符串。这里有两个我用过的例子

findall()感觉更简单、更优雅,但它需要惊人的时间

finditer()可以浏览一个大文件,但我不确定这样做是否正确

下面是一些示例代码。请注意,我感兴趣的实际文本是一个大小约为10MB的字符串,这两种方法之间存在巨大差异

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)
更新将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

标准方法是使用内置

re.sub(reg, rep, text)

顺便说一句,版本之间性能差异的原因是第一个版本中的每次替换都会导致重新编译整个字符串。拷贝速度很快,但当您一次拷贝10 MB时,足够的拷贝速度就会变慢。

您可以,我认为您必须这样做,因为它确实是一个优化的函数,请使用

re.sub(pattern, repl, string[, count, flags])
findall_replace()函数之所以很长,是因为在每次匹配时,都会创建一个新的字符串对象,您将通过执行以下代码看到:

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)
注意,在此代码中,我将
output=text.replace(match,rep)
替换为
text=text.replace(match,rep)
,否则只替换最后一次出现的代码


FindItemer_replace()之所以长,原因与findall_replace()相同:重复创建字符串对象。但是前者使用迭代器re.finditer(),而后者在构造列表对象之前构造,因此它更长。这就是迭代器和非迭代器之间的区别。

顺便说一句,使用findall_replace()的代码是不安全的,它可能会返回未等待的结果:

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

第二个真的快多了?对我来说似乎很奇怪,它们应该需要大约相同的时间。我认为这两种方法都是正确的。你为什么不使用re的sub方法呢?使用带字符串的+=是一种O(n^2)操作,与构建列表和使用“”连接的O(n)操作相比。sören:是的,这两种方法之间的区别是深刻的。这是一个非常简化的示例吗?因为您可以只使用
字符串.replace('dog','cat')
。如果是,实际的正则表达式有多复杂?谢谢。我没有使用re.sub(),因为我认为它的运行方式与findall相同。我再次运行了测试,re.sub显然是最快的方法。结果已添加到问题中。