在大字符串中搜索和替换的最快Python方法

18 投票
3 回答
24618 浏览
提问于 2025-04-16 11:12

我在寻找一种快速的方法来替换一个很大字符串中的许多小字符串。这里有两个我用过的例子。

使用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

3 个回答

4

顺便说一下,你的代码使用了 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
6

你可以这样做,我觉得你应该这样做,因为这确实是一个优化过的函数,使用

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),否则只会替换最后一个出现的部分。

finditer_replace() 之所以也很长,原因和 findall_replace() 一样:都是因为重复创建字符串对象。不过,前者使用了一个迭代器 re.finditer(),而后者则事先构建了一个列表对象,所以它更长。这就是迭代器和非迭代器之间的区别。

28

标准的方法是使用内置的

re.sub(reg, rep, text)

顺便提一下,你的两个版本在性能上的差异,主要是因为你第一个版本中的每次替换都会导致整个字符串被重新复制。复制操作本身很快,但当你一次复制10MB的数据时,复制的次数多了就会变得很慢。

撰写回答