严重正则表达式挂起时间python
我有一个小的(40mb)服务器日志,链接 我有一个正则表达式,我用它来解析代码,这需要花费难以置信的时间(5分钟以上)。我对regex比较陌生,所以我不确定为什么这么小的文件需要这么长时间 下面是一个表达式:严重正则表达式挂起时间python,python,regex,Python,Regex,我有一个小的(40mb)服务器日志,链接 我有一个正则表达式,我用它来解析代码,这需要花费难以置信的时间(5分钟以上)。我对regex比较陌生,所以我不确定为什么这么小的文件需要这么长时间 下面是一个表达式: valid=re.findall(r'(\d+/[a-zA-Z]+/\d+).*?(GET|POST)\s+(http://|https//)([a-zA-Z]+.+?)\.[^/].*?\.([a-zA-Z]+)(/|\s|:).*?\s200\s', line) 当我在这行末尾加上“
valid=re.findall(r'(\d+/[a-zA-Z]+/\d+).*?(GET|POST)\s+(http://|https//)([a-zA-Z]+.+?)\.[^/].*?\.([a-zA-Z]+)(/|\s|:).*?\s200\s', line)
当我在这行末尾加上“200”时,事情真的开始变得一团糟了
下面是完整的代码:
import re
#todo
#specify toplevel domain lookback
######
fhandle=open("access_log.txt", "rU")
access_log=fhandle.readlines()
validfile=open("valid3.txt", "w")
invalidfile=open("invalid3.txt", "w")
valid_dict=dict()
invalid_list=list()
valid_list=list()
#part 1
#read file. apply regex and append into internal data structure (a 2d dictionary)
for line in access_log:
valid=re.findall(r'(\d+/[a-zA-Z]+/\d+).*?(GET|POST)\s+(http://|https//)([a-zA-Z]+.+?)\.[^/].*?\.([a-zA-Z]+)(/|\s|:).*?\s200\s', line)
#valid=re.findall(r'(\d+/[a-zA-Z]+/\d+).*?(GET|POST)\s+(http://|https://)([a-zA-Z]+.+?)\.[^/].*?\.([a-zA-Z]+)(/|\s|:).*?\s+200\s', line)
if valid:
date=valid[0][0]
domain=valid[0][4].lower()
valid_list.append(line)
#writes results into 2d dcitonary (dictionary of dictonaries)
if date not in valid_dict:
valid_dict[date]={}
else:
if domain in valid_dict[date]:
valid_dict[date][domain]+=1
else:
valid_dict[date][domain]=1
#writes remainder files into invalid file log
else:
invalid_list.append(line)
#step 2
#format output file for tsv
#ordered chronologically, with Key:Value pairs orgainzed alphabeticallv by key (Domain Name)
date_workspace=''
domain_workspace=''
for date in sorted(valid_dict.iterkeys()):
date_workspace+=date + "\t"
for domain_name in sorted(valid_dict[date].iterkeys()):
domain_workspace+="%s:%s\t" % (domain_name, valid_dict[date][domain_name])
date_workspace+=domain_workspace
date_workspace+="\n"
domain_workspace=''
# Step 3
# write output
validfile.write(date_workspace)
for line in invalid_list:
invalidfile.write(line)
fhandle.close()
validfile.close()
invalidfile.close()
假设您希望保留域名扩展名,您可以如下更改代码的regex部分:
pattern = re.compile(r'^[^[]+\[(\d+/[a-zA-Z]+/\d+)[^]]+] "(?:GET|POST) https?://[a-zA-Z]+[^?/\s]*\.([a-zA-Z]+)[?/ :][^"]*" 200 ')
for line in access_log:
valid=pattern.search(line)
if valid:
date=valid.group(1)
domain=valid.group(2).lower()
valid_list.append(line)
改进:5分钟->2秒
由于逐行读取文件,一行中只有一个可能的匹配项,因此最好使用返回第一个匹配项的re.search
,而不是re.findall
模式每行使用一次,这就是为什么我选择在循环之前编译模式
模式现在以字符串锚定的开头锚定^
,行的开头现在用[^[]+\[
描述(所有不是[
的都有一次或多次后跟[
)。此改进非常重要,因为它避免了正则表达式引擎尝试在行的每个字符处启动模式
所有*?
都很慢,原因有二(至少):
- 惰性量词必须测试以下子模式是否与每个字符匹配
- 如果模式稍后失败,因为
可以匹配所有字符,正则表达式引擎没有任何理由停止回溯。换句话说,最好的方法是尽可能明确*?
*?
替换为负字符类和贪婪量词
所有未指定的捕获组都已替换为非捕获组(?:…)
其他一些琐碎的更改也被做了,比如(http://https://)=>https?://
或(//\s:)=>[?/:]
。所有\s+
都被一个空格替换
作为旁白,我确信有很多python日志解析器/分析器可以帮助您。还要注意,您的日志文件使用csv格式。这通常是由于嵌套的
+
/*
量词引起的。@user2357112:他的模式中没有嵌套的量词。之后没有找到任何嵌套的量词检查正则表达式。但是,*?
的许多实例可能会产生类似的效果。它仅限于多项式时间问题,而不是指数时间问题,但是仍然有很多方法可以匹配各种*?
模式,如果没有任何一种模式可以使整体匹配工作正常,正则表达式引擎将需要单独执行这些模式。Ac实际上,我可能是在错误地看待经济放缓。这可能只是因为202146行包含大量数据,或者可能是Python的字符串连接优化没有触发,而构建date\u workspace
的代码正在运行。谢谢你,我也很欣赏示例regex。正如我所说的那样使用正则表达式时,我发现我对字符串的大部分内容关心得很少,我只是希望引擎检查下一项。我已经开始在心理上等同于告诉引擎“继续前进,直到找到我的下一个特定组”。有没有更周全的方法来解决这个问题?@Mike:是的,就像我在帖子中解释的那样,方法是在下一个组之前描述内容,例如使用与内容匹配但不包含组的第一个字符的字符类。例如,路径中有一个目录:/.+?/
=>/[^/]+/
我明白了,这是一种不同的思维方式。感谢您的澄清!