我试图从文本文档中提取文件路径(Windows / Ubuntu,相对/绝对)。
使用下面的正则表达式代码检查单词是否是文件路径。
它适用于大多数情况,但对于一个案例失败,它进入无限循环。对此有何解释?
import re
path_regex = re.compile(r'^([\.]*)([/]+)(((?![<>:"/\\|?*]).)+((?<![ .])(\\|/))?)*$' , re.I)
text = '/var/lib/jenkins/jobs/abcd-deploy-test-environment-oneccc/workspace/../workspace/abcd-deploy-test-environment.sh'
path_regex.search(text)
答案 0 :(得分:3)
确实存在问题 你已经叠加了与虚假量词混合的子表达式。
针对斜杠之间的必需部分进行了修改
使用此^([\.]*)([/]+)((?:[^<>:"/\\|?*.\r\n]|\.(?![\\/]))[\\/]?)*$
我们的想法是看看你的防守措施 如果没有一个点,你可以允许前进或后退。
因此,您必须在排除类中包含 dot 和\和/
然后在单独的轮换中对它们进行限定。
如果你这样做,它将永远通过。
^
( [\.]* ) # (1)
( [/]+ ) # (2)
( # (3 start)
(?: # Group start (required between slashes)
[^<>:"/\\|?*.\r\n] # Any character, but exclude these
| # or,
\. # The dot, if not followed by forward or back slash
(?! [\\/] )
) # Group end
[\\/]? # Optional forward or back shash
)* # (3 end)
$
答案 1 :(得分:2)
sln为您的问题提供了一个很好的解决方案,因此我将尝试解释问题是什么。
欢迎来到catastrophic backtracking的欢乐。问题的核心在(((?![<>:"/\\|?*]).)+((?<![ .])(\\|/))?)*
。 (现在我已经说过了,你所有的问题都解决了,对吗?很容易。)
假设你有点像我,并且在第一次有人说&#34;正则表达式回溯&#34;时眨了眨眼几次,我们可以使用较短的输入/path./
来处理你的正则表达式。根据你的正则表达式,这是一个无效的路径,但让我们(有点)轻松地解决问题。
^([\.]*)([/]+)
与前导/
匹配。这很好。
为了便于阅读,我打算调用有问题的捕获组的前半部分((?![<>:"/\\|?*]).)+
,x+
和下半部((?<![ .])(\\|/))?
, y?
。整个小组是(x+y?)
。
(x+y?)*$
后,path./
如何回溯?x+
匹配path.
y?
匹配
(匹配0次,由于?
,这很好)(x+y?)
现已匹配(x+y?)
重复,但失败,因为它与/
不匹配。因此,(x+y?)*
已匹配path.
$
失败,因为它与/
不匹配。(x+y?)*
只能回溯到第一次迭代,因为它只有一次迭代。y?
无法回溯,因为它匹配了0次。x+
回溯,仅匹配path
而不是path.
x+
匹配path
y?
匹配
(x+y?)
现已匹配一次(path
)(x+y?)
重复并再次匹配:
x+
匹配.
y?
匹配
(x+y?)
重复,但由于与/
不匹配而失败。因此,(x+y?)*
已匹配path.
$
失败,因为它与/
不匹配。(x+y?)*
只能回溯到第一次迭代,因为在第二次迭代x+
只匹配一次而y?
匹配0次。y?
匹配0次,因此无法回溯x+
可以回溯到仅匹配pat
(x+y?)
匹配两次:pat
,h.
;然后在下一个回溯中我们pat
h
.
,然后是pa
th.
,依此类推。引擎需要478步才能确定/path./
与正则表达式不匹配。该有问题的捕获组中的每个附加角色都会通过 lot 增加回溯次数,并且在某一点之后,您的正则表达式实现只会放弃并放弃。 sln的解决方案只需49步。
回溯引擎在回溯时的行为很难解释和掌握,特别是在限制为Markdown的情况下,因此我建议您通过a debugger运行正则表达式来查看正在发生的事情。< / p>