【问题标题】:Fastest Python method for search and replace on a large string在大字符串上搜索和替换的最快 Python 方法
【发布时间】:2011-02-04 00:58:39
【问题描述】:

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

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方法?
  • 对字符串使用 += 是 O(n^2) 操作,而构建列表并使用 "" 加入是 O(n)。
  • Sören:是的,这两种方法之间的区别是巨大的。
  • 这是一个非常简化的例子吗?因为你可以使用the_string.replace('dog', 'cat')。如果是,实际的正则表达式有多复杂?

标签: python regex


【解决方案1】:

标准方法是使用内置

re.sub(reg, rep, text)

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

【讨论】:

  • 谢谢。我没有使用 re.sub() 因为我认为它的操作方式与 findall 相同。我再次进行了测试,re.sub 显然是最快的方法。结果已添加到问题中。
【解决方案2】:

你可以,我认为你必须,因为它肯定是一个优化的功能,使用

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() 而后者构造了一个列表对象,所以它更长。这就是迭代器和非迭代器的区别。

【讨论】:

    【解决方案3】:

    顺便说一句,您的代码 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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-11-14
      • 2012-09-09
      • 1970-01-01
      • 2010-11-14
      • 2015-03-19
      • 2020-01-06
      • 2011-05-16
      • 2023-04-06
      相关资源
      最近更新 更多