+ 和 * 之间的 Tcl 贪婪子表达式差异
Tcl greedy subexpression difference between + and *
我正在尝试理解 Tcl 子表达式匹配和 "greediness" 并且完全不知道发生了什么。参考 http://wiki.tcl.tk/396:
中的示例
%regexp -inline (.*?)(n+)(.*) ennui
en e n {}
%regexp -inline ^(.*?)(n+)(.*)$ ennui
ennui e nn ui
尽管我并不完全理解 "nested expressions"(括号表示的是什么,对吗?)匹配,但我决定从小处着手,尝试区分 * 和 + 作为贪婪运算符:
% regexp -inline (.*)(u*)(.*) ennui
ennui ennui {} {}
% regexp -inline (.*)(u+)(.*) ennui
ennui enn u i
如果 * 匹配零个或多个,而 + 匹配一个或多个,我不明白这两个命令之间的输出差异。为什么 u* 和 u+ 在同一个字符串上会产生两个不同的结果?
我觉得这是一个极其重要的细微差别 - 如果我能掌握这个简单模式中发生的事情 match/regex,我的生活就会变得完整。帮助!
提前致谢。
(.*)(u*)(.*)
和 (.*)(u+)(.*)
不同的原因是第二个正则表达式 至少需要 1 个 u
。
Tcl 中的 ARE 正则表达式使用回溯(与大多数 NFA 一样)。使用 (.*)
,引擎从头到尾抓取整个字符串,并开始回溯以查找它是否可以容纳下一个子模式。
在第一个表达式中,u
是可选的(由于 *
可以为 0),因此,贪婪的 .*
决定它不会产生任何字符。然后,最后的.*
也可以匹配0个字符,同样,不需要给那个组任何字符。
在第二个表达式中,u
是必须的,必须至少出现一次。因此,引擎会抓取第一个 .*
的所有字符串,然后回溯并找到 u
。因此,它将起始序列放入第 1 组,并用 (u+)
匹配并捕获 u
。由于 u
只有 1,所以最后的 (.*)
匹配并捕获字符串的其余部分。
@stribizhev 的回答几乎解释了一切。至于你的非贪婪版本——末尾的问号告诉引擎它不应该消耗整个字符串,而是抓住尽可能少的匹配并从那里继续。
(.*?) for "ennui"
匹配 0 个字符,没关系,因为我们不贪心
(n+) for "ennui"
匹配失败,所以引擎returns重新匹配(.*?)
(.*?) for "ennui"
现在匹配一个字符 e
(n+) for "nnui"
匹配 nn
因为它贪婪
(.*) for "ui"
匹配剩下的内容,ui
关于非贪婪。 Tcl 正则表达式有一个怪癖:表达式中的 first 量词设置 whole 表达式的贪婪度。 (参见 re_syntax
manual page 的 "Matching" 部分,密切注意单词“preference”):
A branch has the same preference as the first quantified atom in it which has a preference.
%regexp -inline (.*?)(n+)(.*) ennui
en e n {}
(.*?)
抓取零个或多个字符,更喜欢最短的匹配
(n+)
抓取一个或多个n
,继承最短偏好
(.*)
抓取零个或多个字符,继承最短偏好
第一个子表达式从第一个字符开始匹配,但不包括第一个 n
。第二部分匹配一个n
。第三部分匹配第一个和第二个之间的零个字符 n
.
我有点惊讶第一个子表达式捕获了一个 e
而不是在第一个 n
之前捕获了零个字符,但这可以用 [=50= 的更高优先级来解释] 与正则表达式引擎匹配:
In the event that an RE could match more than one substring of a given string, the RE matches the one starting earliest in the string.
锚定表达式的结果也让我感到惊讶:我本以为 e n nui
而不是 e nn ui
。添加 $
锚点似乎已经放弃了表达式对最短匹配的偏好。
我正在尝试理解 Tcl 子表达式匹配和 "greediness" 并且完全不知道发生了什么。参考 http://wiki.tcl.tk/396:
中的示例%regexp -inline (.*?)(n+)(.*) ennui
en e n {}
%regexp -inline ^(.*?)(n+)(.*)$ ennui
ennui e nn ui
尽管我并不完全理解 "nested expressions"(括号表示的是什么,对吗?)匹配,但我决定从小处着手,尝试区分 * 和 + 作为贪婪运算符:
% regexp -inline (.*)(u*)(.*) ennui
ennui ennui {} {}
% regexp -inline (.*)(u+)(.*) ennui
ennui enn u i
如果 * 匹配零个或多个,而 + 匹配一个或多个,我不明白这两个命令之间的输出差异。为什么 u* 和 u+ 在同一个字符串上会产生两个不同的结果?
我觉得这是一个极其重要的细微差别 - 如果我能掌握这个简单模式中发生的事情 match/regex,我的生活就会变得完整。帮助!
提前致谢。
(.*)(u*)(.*)
和 (.*)(u+)(.*)
不同的原因是第二个正则表达式 至少需要 1 个 u
。
Tcl 中的 ARE 正则表达式使用回溯(与大多数 NFA 一样)。使用 (.*)
,引擎从头到尾抓取整个字符串,并开始回溯以查找它是否可以容纳下一个子模式。
在第一个表达式中,u
是可选的(由于 *
可以为 0),因此,贪婪的 .*
决定它不会产生任何字符。然后,最后的.*
也可以匹配0个字符,同样,不需要给那个组任何字符。
在第二个表达式中,u
是必须的,必须至少出现一次。因此,引擎会抓取第一个 .*
的所有字符串,然后回溯并找到 u
。因此,它将起始序列放入第 1 组,并用 (u+)
匹配并捕获 u
。由于 u
只有 1,所以最后的 (.*)
匹配并捕获字符串的其余部分。
@stribizhev 的回答几乎解释了一切。至于你的非贪婪版本——末尾的问号告诉引擎它不应该消耗整个字符串,而是抓住尽可能少的匹配并从那里继续。
(.*?) for "ennui"
匹配 0 个字符,没关系,因为我们不贪心(n+) for "ennui"
匹配失败,所以引擎returns重新匹配(.*?)
(.*?) for "ennui"
现在匹配一个字符e
(n+) for "nnui"
匹配nn
因为它贪婪(.*) for "ui"
匹配剩下的内容,ui
关于非贪婪。 Tcl 正则表达式有一个怪癖:表达式中的 first 量词设置 whole 表达式的贪婪度。 (参见 re_syntax
manual page 的 "Matching" 部分,密切注意单词“preference”):
A branch has the same preference as the first quantified atom in it which has a preference.
%regexp -inline (.*?)(n+)(.*) ennui
en e n {}
(.*?)
抓取零个或多个字符,更喜欢最短的匹配(n+)
抓取一个或多个n
,继承最短偏好(.*)
抓取零个或多个字符,继承最短偏好
第一个子表达式从第一个字符开始匹配,但不包括第一个 n
。第二部分匹配一个n
。第三部分匹配第一个和第二个之间的零个字符 n
.
我有点惊讶第一个子表达式捕获了一个 e
而不是在第一个 n
之前捕获了零个字符,但这可以用 [=50= 的更高优先级来解释] 与正则表达式引擎匹配:
In the event that an RE could match more than one substring of a given string, the RE matches the one starting earliest in the string.
锚定表达式的结果也让我感到惊讶:我本以为 e n nui
而不是 e nn ui
。添加 $
锚点似乎已经放弃了表达式对最短匹配的偏好。