我想使用Pythons regex模块匹配字符串。
在我的情况下,我想验证字符串是否以“_”组合开头,结尾和由大写字母组成。例如,以下字符串有效:“MY_HERO2”。以下字符串无效:“_MY_HREO2”,“我的HERO2”,“MY_HERO2 _”
要验证字符串,请使用以下代码:
import re
my_string = "MY_HERO"
p = re.compile("^([A-Z,0-9]+_??)+[A-Z,0-9]$")
if p.match(my_string):
print "validated"
那么我的问题是什么?验证包含空格的长字符串非常非常慢。我怎么能避免这个?我的模式错了吗?这种行为的原因是什么?
以下是一些数字:
MY_HERO2 --> 53 ms
MY_SUPER_GREAT_UNBELIEVABLE_HERO --> 69 microseconds
MY_SUPER_GREAT_UNBELIEVABLE HERO --> 223576 microseconds
MY_SUPER_GREAT_UNBELIEVABLE_STRONG_HERO --> 15 microseconds
MY_SUPER_GREAT_UNBELIEVABLE_STRONG HERO --> 979429 microseconds
感谢您的提名和回复。 :-) 保罗
答案 0 :(得分:13)
那我的问题是什么?
问题是catastrophic backtracking。正则表达式引擎尝试了很多变化,这需要花费很多时间。
让我们用一个非常简单的例子来试试:A_B D
。
引擎首先将A
与[A-Z,0-9]+
匹配,然后它会尝试_??
,但由于它是可选的(懒惰),它现在会跳过它,这个已完成([A-Z,0-9]+_??)+
。
现在引擎尝试匹配[A-Z,0-9]
,但字符串中有一个_
因此它失败了,现在它需要回溯,所以它重新进入([A-Z,0-9]+_??)+
失败了上次尝试_??
并成功。
现在引擎再次退出([A-Z,0-9]+_??)+
并尝试匹配[A-Z,0-9]
并成功,然后尝试匹配字符串结尾$
但失败,现在它回溯并进入{ {1}}再次。
我希望你能看到它的发展方向,因为我已经分层编写它并且我们还没有到达空格字符 - 实际上你的正则表达式中没有接受的任何字符,例如([A-Z,0-9]+_??)+
或{{ 1}}等会产生这个,而不仅仅是空白 - 这是一个很小的例子,在你的长字符串的情况下,它将不得不这样做数百次,直到它能够匹配整个字符串或失败,因此大量的时间。
验证包含空格的长字符串非常非常慢。
这也是由于回溯和地狱般的变化。
我该如何避免这种情况?
您可以改用此正则表达式:
#
这可确保字符串以大写或数字开头,后跟可选的%
,重复此操作一次或多次,并确保最后有一个大写或数字。
我的模式错了吗?这种行为的原因是什么?
你的表达没错,但效率很低。
^([A-Z0-9]_?)*[A-Z0-9]$
答案 1 :(得分:0)
使用这个简单的正则表达式可以获得相同的结果:
import timeit
stmt = '''
import re
reg = '^(?:[A-Z0-9][A-Z0-9_]*)?[A-Z0-9]$'
p = re.compile(reg)
p.match('%s')
'''
str_list = ['MY_HERO2', 'MY_SUPER_GREAT_UNBELIEVABLE_HERO', 'MY_SUPER_GREAT_UNBELIEVABLE HERO', 'MY_SUPER_GREAT_UNBELIEVABLE_STRONG_HERO', 'MY_SUPER_GREAT_UNBELIEVABLE_STRONG HERO']
for s in str_list:
t = timeit.timeit(stmt%s, number=1000)
print '%s: %s' % (s, t)
import re
reg = '^(?:[A-Z0-9][A-Z0-9_]*)?[A-Z0-9]$'
p = re.compile(reg)
for s in str_list:
result = p.match(s) is not None
print '%s: %s' % (s, result)
结果:
MY_HERO2: 0.00375986099243
MY_SUPER_GREAT_UNBELIEVABLE_HERO: 0.00417900085449
MY_SUPER_GREAT_UNBELIEVABLE HERO: 0.00534510612488
MY_SUPER_GREAT_UNBELIEVABLE_STRONG_HERO: 0.00419306755066
MY_SUPER_GREAT_UNBELIEVABLE_STRONG HERO: 0.00567102432251
MY_HERO2: True
MY_SUPER_GREAT_UNBELIEVABLE_HERO: True
MY_SUPER_GREAT_UNBELIEVABLE HERO: False
MY_SUPER_GREAT_UNBELIEVABLE_STRONG_HERO: True
MY_SUPER_GREAT_UNBELIEVABLE_STRONG HERO: False