Tcl贪婪的子表达式+和*之间的差异

时间:2015-07-23 13:12:06

标签: regex tcl regex-greedy

我试图了解Tcl子表达式匹配和"贪婪"并且我完全不知道发生了什么。引用http://wiki.tcl.tk/396上的示例:

%regexp -inline (.*?)(n+)(.*) ennui
en e n {} 
%regexp -inline ^(.*?)(n+)(.*)$ ennui
ennui e nn ui

尽管我不完全理解"嵌套表达式" (这是括号表示的,对吗?)匹配,我决定从小开始,尝试将*和+之间的区别作为贪婪的运算符:

% regexp -inline (.*)(u*)(.*) ennui
ennui ennui {} {}
% regexp -inline (.*)(u+)(.*) ennui
ennui enn u i

如果*匹配零或更多,+匹配一个或多个,我不明白两个命令之间输出的差异。为什么u *和u +在同一个字符串上产生两个不同的结果?

我觉得这是一个非常重要的细微差别 - 如果我能够掌握这个简单模式匹配/正则表达式中发生的事情,我的生活将会变得完整。救命!

提前致谢。

3 个答案:

答案 0 :(得分:5)

关于非贪婪。 Tcl正则表达式有一个怪癖:表达式中的第一个量词设置了整个表达式的贪婪。 (请参阅re_syntax manual page的“匹配”部分,密切关注“偏好”一词:)

  

分支具有与其中具有偏好的第一个量化原子相同的偏好。

%regexp -inline (.*?)(n+)(.*) ennui
en e n {} 
  • (.*?)抓取零个或多个字符,更喜欢最短匹配
  • (n+)抓取一个或多个n,继承最短的偏好
  • (.*)抓取零个或多个字符,继承最短的偏好

第一个子表达式匹配第一个字符,但不包括第一个n。第二部分匹配一个n。第三部分匹配第一个和第二个n之间的零个字符。

我有点惊讶的是,第一个子表达式捕获了e而不是在第一个n之前捕获零个字符,但这可以通过“最左边”匹配到的最高优先级来解释正则表达式引擎:

  

如果RE可以匹配给定字符串的多个子字符串,则RE匹配字符串中最早开始的字符串。

achored表达式的结果也让我感到惊讶:我原本期望e n nui而不是e nn ui。添加$锚似乎已经放弃了表达式对最短匹配的偏好。

答案 1 :(得分:3)

(.*)(u*)(.*)(.*)(u+)(.*)差异的原因是第二个正则表达式至少需要1个u

Tcl中的ARE正则表达式使用回溯(与大多数NFA一样)。使用(.*),引擎从头到尾抓取整个字符串,并开始回溯以查找它是否可以容纳下一个子模式。

在第一个表达式中,u是可选的(由于*可以为0),因此,贪婪的.*决定它不会产生任何字符。然后,最后.*也可以匹配0个字符,同样,不需要为该组提供任何字符。

在第二个表达式中,u是强制性的,必须至少出现一次。因此,引擎会使用第一个.*抓取所有字符串,然后回溯并找到u。因此,它将起始序列放入组1,并将u(u+)进行匹配和捕获。由于u仅为1,因此最后(.*)匹配并捕获字符串的其余部分。

答案 2 :(得分:0)

@stribizhev答案几乎解释了一切。至于你的非贪婪版本 - 最后的问号告诉引擎它不应该消耗整个字符串,但抓住尽可能少的匹配并从那里继续。

  • (.*?) for "ennui"匹配0个字符,没关系,因为我们不贪心
  • (n+) for "ennui"匹配失败,因此引擎再次返回匹配(.*?)
  • (.*?) for "ennui"现在匹配一个字符e
  • (n+) for "nnui"匹配nn,因为它贪婪
  • (.*) for "ui"匹配剩下的ui