使用 GNU sed(-r
为了清楚起见,使用标志),输入字符串上的以下两个替换ab
给出相同的结果:
s/(.)(.)|(.)(.)$/\2\1\3\4/
Run Code Online (Sandbox Code Playgroud)
和
s/(.)(.)$|(.)(.)/\1\2\4\3/
Run Code Online (Sandbox Code Playgroud)
两者都给ba
. 看起来,替代方案(.)(.)
(没有 的那个$
)在两次替换中都成功,无论其位置是第一个还是第二个替代方案。为什么会这样呢?这种替代方案的决定因素是什么?
正则表达式的 POSIX 规范指定1当替代方案从不同位置开始时(在这种情况下,优先选择较早的一个),以及当它们从相同位置开始但具有不同长度(优先选择较长的一个)时,决胜局,但是当两个替代方案从相同位置开始且具有相同长度时,它似乎没有指定捕获组的行为,因此将其留给具体实现。
对匹配序列的搜索从字符串的开头开始,并在找到与表达式匹配的第一个序列时停止,其中“first”被定义为表示“在字符串中最早开始”。如果模式允许可变数量的匹配字符,因此从该点开始有多个这样的序列,则匹配最长的这样的序列。[...] – The Open Group 基本规范第 7 期,2018 年版
这是该现象的一个运行示例。
s/(.)(.)|(.)(.)$/\2\1\3\4/
Run Code Online (Sandbox Code Playgroud)
假设一个答案。
给定输入行ab
, 和(.)(.)
都(.)(.)$
将匹配ab
相同的长度2。因此,正如您在问题中所说,两个正则表达式从同一起点匹配相同的长度。
但是,我想说的是,目前(.)(.)
与 匹配ab
,引擎还需要进行一项检查(针对$
),以检查是否(.)(.)$
也匹配(即是否在 EOL 处匹配),在这种情况下,后一个正则表达式将不会无论如何,它都是首选,因为它与已经匹配的前正则表达式具有相同的长度并且从同一点开始。(.)(.)
因此,对我来说,引擎返回对without中的组的引用是有意义的$
。
我认为我的这个推理意味着匹配对可打印字符是贪婪的,但对不可打印字符是懒惰的。
与此比较/对比:
echo ab|sed -r 's/^(.)(.)|(.)(.)/\2\1\3\4/'
ba
Run Code Online (Sandbox Code Playgroud)
echo ab|sed -r 's/(.)(.)|^(.)(.)/\2\1\3\4/'
ba
Run Code Online (Sandbox Code Playgroud)
其中最左边的正则表达式在两种情况下都匹配。