这看起来像一个简单的正则表达式,没有反向引用,没有“任何”字符,我甚至敢说Thomson DFA和所有人都可以解析它。它甚至可以工作,但是非常简单的不匹配就会窒息。
{\s*?
ngx_string\("(?P<name>[a-z0-9_]+)"\)\s*?,\s*?
(?P<where>(([A-Z0-9_]+)\s*\|?)+?)\s*?,\s*?
(?P<bla>[^\n}]+?)\s*?,\s*?
(?P<bla2>[^\n}]+?)\s*?,\s*?
(?P<bla3>[^\n}]+?)\s*?,\s*?
(?P<bla4>[^\n}]+?)\s*?
}
+ re.MULTILINE | re.VERBOSE
我目前正在Python 2.7.8上尝试这个(但链接的要点也在py3.4上失败;而且linux,x86-64,Ubuntu,PCRE静态链接在[至少/ proc // maps不会显示任何有趣的东西)。
这解析得很好:
{ ngx_string("daemon"),
NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
0,
offsetof(ngx_core_conf_t, daemon),
NULL },
这就是有趣的地方:
{ ngx_string("off"), NGX_HTTP_REQUEST_BODY_FILE_OFF },
{ ngx_string("on"), NGX_HTTP_REQUEST_BODY_FILE_ON },
此外,还有更多数据:
将第二行更改为此
(?P<where>(([A-Z0-9_]{1,20})\s*\|?){1,6}?)\s{0,10}?,\s{0,10}?
,它最终在合理的时间内完成,但指数爆炸仍然存在,只是可以忍受:
trying { ngx_string("off"), NGX_HTTP_REQUEST_BODY_FILE
Took 0.033483 s
trying { ngx_string("off"), NGX_HTTP_REQUEST_BODY_FILE_
Took 0.038528 s
trying { ngx_string("off"), NGX_HTTP_REQUEST_BODY_FILE_O
Took 0.044108 s
trying { ngx_string("off"), NGX_HTTP_REQUEST_BODY_FILE_OF
Took 0.053547 s
另外,有趣的是,基于JS的Python正则表达式(模拟器?)解析器可以像PCRE冠军的早餐一样吃它:https://www.debuggex.com/r/S__vSvp8-LGLuCLQ
哦,也许有人应该创建pathological-regex标签:|
答案 0 :(得分:1)
(?P<where>(([A-Z0-9_]+)\s*\|?)+?)
^ ^
这是你的正则表达式爆炸的地方。阅读http://www.regular-expressions.info/catastrophic.html。
在每次失败时,它会返回一步以检查是否存在匹配。这会为正则表达式引擎创建一系列步骤和可能性。
答案 1 :(得分:1)
问题在于原始正则表达式中的(([A-Z0-9_]+)\s*\|?)+?
或测试正则表达式中的(([A-Z0-9_]{1,20})\s*\|?){1,6}?
。 {1,20}
和{1,6}
仅用于抑制指数回溯。
由于\s*
和\|?
是可选的,当输入仅包含(([A-Z0-9_]+))+?
不带空格或条[A-Z0-9_]
时,正则表达式可以扩展为指数回溯|
的经典示例1}},但与正则表达式的其余部分不匹配。
例如,匹配(?P<where>(([A-Z0-9_]+)\s*\|?)+?)\s*?,\s*?
与AAAAAAAAAAAAAA
(,
缺失)会导致引擎检查所有分割字符串的可能性,每个标记在不同的迭代次数下匹配外部重复:
AAAAAAAAAAAAAA
AAAAAAAAAAAAA A
AAAAAAAAAAAA AA
AAAAAAAAAAAA A A
AAAAAAAAAAA AAA
...
A AAAAAAAAAAAAA
A AAAAAAAAAAAA A
A AAAAAAAAAAA AA
...
A A A A A A A A A A A A A A
仔细看看,你的正则表达式的其余部分也有过多的回溯问题。
以(?P<bla2>[^\n}]+?)\s*?,\s*?
为例。 [^\n}]
可以匹配空格(或制表符或其他一些空格字符),\s
也可以。如果您的不匹配输入包含一长串空格,这可能会导致过多的回溯。可能还存在正确性问题,因为,
也可以与[^\n}]
匹配。
另外,Python re
是一个NFA引擎,没有任何优化来缓解指数回溯问题。
减轻指数回溯:
{\s*
ngx_string\("(?P<name>[a-z0-9_]+)"\)\s*,\s*
(?P<where>[A-Z0-9_]+(?:\s*\|\s*[A-Z0-9_]+)*)\s*,\s*
(?P<bla>[^\n}]+?)\s*,\s*
(?P<bla2>[^\n}]+?)\s*,\s*
(?P<bla3>[^\n}]+?)\s*,\s*
(?P<bla4>[^\n}]+?)\s*
}
[^\n}]
的过度回溯和正确性问题仍然没有解决。如果参数不在不同的行上,则函数调用中的,
可能被错误地识别为外部块{}
的一部分。
一般解决方案需要递归正则表达式,因为您可以调用函数将其结果作为参数传递,例如offsetof(ngx_core_conf_t, daemon)
,但re
包中没有递归正则表达式功能。一般性较低的解决方案是匹配嵌套括号的某些级别,例如1级嵌套:
(?P<bla>(?:\([^)]*\)|[^,()])+),\s*
整个正则表达式是:
{\s*?
ngx_string\("(?P<name>[a-z0-9_]+)"\)\s*,\s*
(?P<where>[A-Z0-9_]+(?:\s*\|\s*[A-Z0-9_]+)*)\s*,
(?P<bla>(?:\([^)]*\)|[^,()])+),\s*
(?P<bla2>(?:\([^)]*\)|[^,()])+),\s*
(?P<bla3>(?:\([^)]*\)|[^,()])+),\s*
(?P<bla4>(?:\([^)]*\)|[^,()])+)
}
有两个警告:
<bla*>
捕获组最后会包含空格。如果你想删除空格,正则表达式会更长一些,和可以防止可能的过度回溯。您可以尝试在\s*
之前将,
添加回this demo here。()
不是任何字符串文字的一部分。