在Python中对正则表达式使用compile有什么好处吗?
h = re.compile('hello')
h.match('hello world')
vs
re.match('hello', 'hello world')
在Python中对正则表达式使用compile有什么好处吗?
h = re.compile('hello')
h.match('hello world')
vs
re.match('hello', 'hello world')
当前回答
根据Python文档:
序列
prog = re.compile(pattern)
result = prog.match(string)
等于
result = re.match(pattern, string)
但是,当表达式将在一个程序中多次使用时,使用re.compile()并保存生成的正则表达式对象以供重用会更有效。
所以我的结论是,如果你要为许多不同的文本匹配相同的模式,你最好预编译它。
其他回答
在无意中看到这里的讨论之前,我运行了这个测试。然而,在运行它之后,我想我至少会发布我的结果。
我剽窃了Jeff Friedl的“精通正则表达式”中的例子。这是在一台运行OSX 10.6 (2Ghz英特尔酷睿2双核,4GB内存)的macbook上。Python版本为2.6.1。
运行1 -使用re.compile
import re
import time
import fpformat
Regex1 = re.compile('^(a|b|c|d|e|f|g)+$')
Regex2 = re.compile('^[a-g]+$')
TimesToDo = 1000
TestString = ""
for i in range(1000):
TestString += "abababdedfg"
StartTime = time.time()
for i in range(TimesToDo):
Regex1.search(TestString)
Seconds = time.time() - StartTime
print "Alternation takes " + fpformat.fix(Seconds,3) + " seconds"
StartTime = time.time()
for i in range(TimesToDo):
Regex2.search(TestString)
Seconds = time.time() - StartTime
print "Character Class takes " + fpformat.fix(Seconds,3) + " seconds"
Alternation takes 2.299 seconds
Character Class takes 0.107 seconds
运行2 -不使用re.compile
import re
import time
import fpformat
TimesToDo = 1000
TestString = ""
for i in range(1000):
TestString += "abababdedfg"
StartTime = time.time()
for i in range(TimesToDo):
re.search('^(a|b|c|d|e|f|g)+$',TestString)
Seconds = time.time() - StartTime
print "Alternation takes " + fpformat.fix(Seconds,3) + " seconds"
StartTime = time.time()
for i in range(TimesToDo):
re.search('^[a-g]+$',TestString)
Seconds = time.time() - StartTime
print "Character Class takes " + fpformat.fix(Seconds,3) + " seconds"
Alternation takes 2.508 seconds
Character Class takes 0.109 seconds
用下面的例子:
h = re.compile('hello')
h.match('hello world')
上面例子中的匹配方法和下面的不一样:
re.match('hello', 'hello world')
Re.compile()返回一个正则表达式对象,这意味着h是一个正则表达式对象。
regex对象有自己的匹配方法,带有可选的pos和endpos参数:
的。匹配(字符串[线程][线程]])
pos
可选的第二个参数pos给出了字符串中的一个索引 搜寻就要开始了;缺省值为0。这并不完全是 相当于对字符串进行切片;'^'模式字符匹配于 字符串的真正开始和在a之后的位置 换行符,但不一定在搜索到的索引处 开始。
尾部
可选参数endpos限制了字符串的长度 搜索;这就好像字符串有endpos个字符那么长 只搜索从pos到endpos - 1的字符 匹配。如果endpos小于pos,则找不到匹配;否则, 如果rx是编译后的正则表达式对象,则rx。搜索(字符串,0, 50)等于rx。搜索(字符串(:50),0)。
regex对象的search、findall和finditer方法也支持这些参数。
Re.match (pattern, string, flags=0)不支持,如你所见, 它的search、findall和finditer也没有。
match对象具有补充这些参数的属性:
match.pos
的search()或match()方法传递的pos的值 一个正则表达式对象。这是正则表达式所在字符串的索引 引擎开始寻找匹配。
match.endpos
传递给search()或match()方法的endpos值 正则表达式对象的。对象超出的字符串的索引 RE引擎不会去。
一个regex对象有两个唯一的,可能有用的属性:
regex.groups
模式中捕获组的数量。
regex.groupindex
将(?P)定义的任何符号组名映射到的字典 组数字。如果没有使用符号组,则字典为空 在模式中。
最后,match对象有这个属性:
match.re
其match()或search()方法的正则表达式对象 生成此匹配实例。
我同意诚实的亚伯,所给例子中的匹配(…)是不同的。他们不是一对一的比较,因此,结果是不同的。为了简化我的回答,我用A, B, C, D来表示这些函数。哦,是的,我们在re.py中处理的是4个函数而不是3个。
运行这段代码:
h = re.compile('hello') # (A)
h.match('hello world') # (B)
与运行此代码相同:
re.match('hello', 'hello world') # (C)
因为,当查看源代码re.py时,(A + B)意味着:
h = re._compile('hello') # (D)
h.match('hello world')
(C)实际上是:
re._compile('hello').match('hello world')
因此,(C)与(B)并不相同,实际上(C)在调用(D)之后调用(B), (D)也被(A)调用,换句话说,(C) = (A) + (B),因此,在循环中比较(A + B)与在循环中比较(C)的结果相同。
George的regexTest.py为我们证明了这一点。
noncompiled took 4.555 seconds. # (C) in a loop
compiledInLoop took 4.620 seconds. # (A + B) in a loop
compiled took 2.323 seconds. # (A) once + (B) in a loop
大家的兴趣是,如何得到2.323秒的结果。为了确保compile(…)只被调用一次,我们需要将编译后的regex对象存储在内存中。如果使用类,则可以存储对象,并在每次调用函数时重用该对象。
class Foo:
regex = re.compile('hello')
def my_function(text)
return regex.match(text)
如果我们不使用类(这是我今天的要求),那么我没有评论。我还在学习如何在Python中使用全局变量,我知道全局变量不是什么好东西。
还有一点,我认为使用(A) + (B)的方法有优势。以下是我观察到的一些事实(如果我错了,请指正):
Calls A once, it will do one search in the _cache followed by one sre_compile.compile() to create a regex object. Calls A twice, it will do two searches and one compile (because the regex object is cached). If the _cache gets flushed in between, then the regex object is released from memory and Python needs to compile again. (someone suggests that Python won't recompile.) If we keep the regex object by using (A), the regex object will still get into _cache and get flushed somehow. But our code keeps a reference on it and the regex object will not be released from memory. Those, Python need not to compile again. The 2 seconds difference in George's test compiled loop vs compiled is mainly the time required to build the key and search the _cache. It doesn't mean the compile time of regex. George's reallycompile test show what happens if it really re-do the compile every time: it will be 100x slower (he reduced the loop from 1,000,000 to 10,000).
以下是(A + B)比(C)更好的情况:
如果可以在类中缓存regex对象的引用。 如果需要重复调用(B)(在循环内或多次),则必须在循环外缓存对regex对象的引用。
如果(C)足够好:
不能缓存引用。 我们只是偶尔用一次。 总的来说,我们没有太多的正则表达式(假设编译后的正则表达式永远不会被刷新)
简单回顾一下,以下是abc:
h = re.compile('hello') # (A)
h.match('hello world') # (B)
re.match('hello', 'hello world') # (C)
感谢阅读。
有趣的是,编译对我来说确实更有效(Win XP上的Python 2.5.2):
import re
import time
rgx = re.compile('(\w+)\s+[0-9_]?\s+\w*')
str = "average 2 never"
a = 0
t = time.time()
for i in xrange(1000000):
if re.match('(\w+)\s+[0-9_]?\s+\w*', str):
#~ if rgx.match(str):
a += 1
print time.time() - t
按原样运行上述代码一次,并以相反的方式运行两个if行,编译后的正则表达式的速度将提高一倍
我有很多运行一个编译过的正则表达式和实时编译的经验,并没有注意到任何可感知的差异。显然,这只是传闻,当然也不是反对编译的有力论据,但我发现两者之间的差异可以忽略不计。
编辑: 在快速浏览了实际的Python 2.5库代码后,我发现无论何时使用正则表达式(包括调用re.match()), Python都会在内部编译和缓存正则表达式,因此实际上只在正则表达式被编译时进行更改,并且不应该节省太多时间——只节省检查缓存所需的时间(对内部dict类型的键查找)。
来自re.py模块(评论是我的):
def match(pattern, string, flags=0):
return _compile(pattern, flags).match(string)
def _compile(*key):
# Does cache check at top of function
cachekey = (type(key[0]),) + key
p = _cache.get(cachekey)
if p is not None: return p
# ...
# Does actual compilation on cache miss
# ...
# Caches compiled regex
if len(_cache) >= _MAXCACHE:
_cache.clear()
_cache[cachekey] = p
return p
我仍然经常预编译正则表达式,但只是为了将它们绑定到一个漂亮的、可重用的名称,而不是为了任何预期的性能提升。