正则表达式比使用.replace()更快

时间:2012-01-22 01:27:14

标签: python regex algorithm

正则表达式会比下面的Python代码更快吗?

myStr.replace(","," ").replace("'"," ").replace("-","").replace("_","").replace("["," ").replace("]"," ")

这是否意味着str被迭代超过6次(replace()调用次数)?定期预测会更快或者算法/方法是否相同?

这个正则表达式会是什么样的?

5 个答案:

答案 0 :(得分:7)

简短回答:使用str.translate,它比标准Python发行版中提供的任何替代版本都快。

答案很长:

  

这是否意味着str被迭代超过6次(数字   of replace()调用??

还有什么意思呢?

  

这个正则表达式会是什么样的?

您遇到问题的哪个部分?

作为一个可以理解的等价物,试试这个:

"".join(" " if c in ",'-_[]" else c for c in myStr)

或者这个:

cset = set(",'-_[]")
"".join(" " if c in cset else c for c in myStr)

就速度而言,你应该做一些时间,数据类似于你期望的数据。不要忘记在字符串中没有此类字符的情况下包含测试。

更新以下是实际工作的代码:

>>> import re
>>> myStr = "a,b'c-d_f[g]h"
>>> re.sub(r"[,'\-_[\]]", " ", myStr)
'a b c d f g h'
>>>

更一般地说,为了避免计算出哪些角色需要转义,你可以这样做:

>>> badchars = ",'-_[]"
>>> regex = "[" + re.escape(badchars) + "]"
>>> print regex
[\,\'\-\_\[\]]
>>> re.sub(regex, " ", myStr)
'a b c d f g h'
>>>

更新2 要在一个正则表达式中执行OP问题似乎要求的内容:

>>> re.sub(r"[,'\-_[\]]", lambda m: "" if m.group(0) in "-_" else " ", myStr)
'a b cdf g h'

更新3 使用str.translate

以最快的方式执行OP问题似乎要求的内容
# Python 2.X
>>> myStr = "a,b'c-d_f[g]h"
>>> chars2space = ",'[]"
>>> chars2delete = "-_"
>>> table = "".join(" " if chr(i) in chars2space else chr(i) for i in xrange(256))
>>> myStr.translate(table, chars2delete)
'a b cdf g h'

# Python 3.x
>>> myStr = "a,b'c-d_f[g]h"
>>> chars2space = ",'[]"
>>> chars2delete = "-_"
>>> table = dict((ord(c), " ") for c in chars2space)
>>> table.update(dict((ord(c), None) for c in chars2delete))
>>> myStr.translate(table)
'a b cdf g h'
>>>

更新4 我更新了Blair的计时器小工具,以包含我认真的解决方案(str.translate)。以下是运行标准win32 Python 2.7.2的无名英特尔机箱上的结果:

# input = "a,b'c-d_f[g]h"
String replacement         : 0.00000195 seconds per replacement ( 1.00 X)
Borodin: two-regex         : 0.00000429 seconds per replacement ( 2.20 X)
John Machin: regex/lambda  : 0.00000489 seconds per replacement ( 2.51 X)
John Machin: str.translate : 0.00000042 seconds per replacement ( 0.22 X)

# input *= 100
String replacement         : 0.00001612 seconds per replacement ( 1.00 X)
Borodin: two-regex         : 0.00015821 seconds per replacement ( 9.82 X)
John Machin: regex/lambda  : 0.00036253 seconds per replacement (22.50 X)
John Machin: str.translate : 0.00000424 seconds per replacement ( 0.26 X)

# input *= 1000
String replacement         : 0.00012404 seconds per replacement ( 1.00 X)
Borodin: two-regex         : 0.00148683 seconds per replacement (11.99 X)
John Machin: regex/lambda  : 0.00360127 seconds per replacement (29.03 X)
John Machin: str.translate : 0.00003361 seconds per replacement ( 0.27 X)

# input = "nopunctuation" * 1000 i.e. same length as previous results
String replacement         : 0.00002708 seconds per replacement ( 1.00 X)
Borodin: two-regex         : 0.00018181 seconds per replacement ( 6.71 X)
John Machin: regex/lambda  : 0.00008235 seconds per replacement ( 3.04 X)
John Machin: str.translate : 0.00001780 seconds per replacement ( 0.66 X)

看起来str.translate就在眼前。

答案 1 :(得分:4)

出于兴趣,我使用timeit对其他答案中给出的方法进行了快速计时测试。正则表达式都是预先编译为安全的。我的上网本(Ubuntu 11.10,Python 2.7.2)上的结果,从最快到最慢:

String replacement: 8.556e-06 seconds per replacement
Borodin's two-regex solution: 1.979e-05 seconds per replacement
John Machin's regex/lambda solution: 2.623e-05 seconds per replacement

因此,双正则解决方案比简单的字符串替换慢2.3倍,而John Machin的单正则表达式和lambda函数解决方案比字符串替换慢3.06倍。

要使用更长的字符串进行测试,我将原始字符串连接100次:

String replacement: 7.600e-05 seconds per replacement
Borodin's two-regex solution: 7.894e-04 seconds per replacement
John Machin's regex/lambda solution: 1.909e-03 seconds per replacement

双正则表达式比字符串替换慢10倍,正则表达式/ lambda慢25倍。

然后将原始输入连接1000次:

String replacement: 5.396e-04 seconds per replacement
Borodin's two-regex solution: 8.584e-03 seconds per replacement
John Machin's regex/lambda solution: 2.094e-02 seconds per replacement

双正则表达式现在比字符串替换慢15.9倍,正则表达式/ lambda慢38.8倍。

似乎输入变得越来越长,正则表达式相比变得越来越慢。我认为这是因为正则表达式还必须按字符扫描输入字符,显然,它们执行的更复杂的测试比循环几次要慢。使用lambda函数用单个正则表达式替换字符似乎比使用两个正则表达式慢得多。

任何想要检查机器计时的人的代码:

import sys
import timeit

# How many times to test each method.
n = 1000

# String to test with.
test_str = "a,b'c-d_f[g]h"

# Correct output.
correct = "a b cdf g h"

# Uncomment to try longer strings.
#test_str *= 100
#correct *= 100

# Setup code (i.e., code which shouldn't be included in the timings).
setup = """
import re

# Borodin's two-regex solution.
bre1 = re.compile(r"[,'\[\]]")
bre2 = re.compile(r"[-_]")

# John Machin's solution.
jmre = re.compile(r"[,'\-_[\]]")
repl = lambda m: "" if m.group(0) in "-_" else " "

test_str=\"{0:s}\"""".format(test_str)

# The methods.
methods = {
    'String replacement': 'result=test_str.replace(","," ").replace("\'"," ").replace("-","").replace("_","").replace("["," ").replace("]"," ")',
    'Borodin\'s two-regex solution': 'result=bre2.sub("", bre1.sub(" ", test_str))',
    'John Machin\'s regex/lambda solution': 'result=jmre.sub(repl, test_str)',
}

# Execute the setup so we can test for correctness.
exec(setup)

for method, code in methods.items():
    # Check the code gives correct results.
    exec(code)
    if(result != correct):
        sys.stdout.write('{0} gave incorrect output: {1}\n'.format(method, result))
        continue

    # Time it.
    t = timeit.timeit(code, setup, number=n)
    sys.stdout.write('{0}: {1:.3e} seconds per replacement\n'.format(method, t/n))

答案 2 :(得分:2)

正则表达式可能会更快。一个好的正则表达式引擎(和Python有一个很好的)是一种非常快速的方法来完成它可以处理的各种字符串转换。除非你对正则表达式真的很好,否则理解起来会有点难度。

通常,当您第一次编写项目时,您应该优化程序员时间(即您的时间),而不是运行时。你花在考虑两个正则表达式调用是否比6个替换调用更快的时间可能更好地花在编写更多代码上。

如果这个问题是因为你完成了你的程序(ish)并且开始运行它,并且发现它太慢了,那么现在是思考这些事情的好时机,尽管你可能需要制作一系列性能测量,以找出运行时所在的位置。程序中的大部分运行时很可能都花费在代码的相对较小的部分中,因此一旦您可以识别它们,您就可以集中精力使它们更快。我的直觉是这些替换呼叫不是这样的位置。但是,即使是非常有经验的程序员也非常不善于预测代码中的“热点”所在的位置。

如果,OTOH,这个问题是因为您刚刚编写了替换调用而被提示,并且想知道是否可以以更快的方式执行此操作,您应该继续前进并编写更多代码。毕竟,那是编程的有趣之处。 :)

答案 3 :(得分:1)

我相信,在这种情况下,正则表达式会更快,因为在使用replace时,每次都必须迭代整个字符串。

答案 4 :(得分:0)

我会说正则表达式几乎肯定会更快,但你应该做一个简单的基准测试来确定。您拥有的代码执行以下单独的步骤

myStr.replace(","," ")
myStr.replace("'"," ")
myStr.replace("-","")
myStr.replace("_","")
myStr.replace("["," ")
myStr.replace("]"," ")

可以使用正则表达式分两步完成:

re.sub(r"[,'\[\]]", " ", myStr)
re.sub(r"[-_]", "", myStr)