5

使用 GNU sed(-r为了清楚起见,带有标志),输入字符串上的以下两个替换ab给出了相同的结果:

s/(.)(.)|(.)(.)$/\2\1\3\4/

s/(.)(.)$|(.)(.)/\1\2\4\3/

都给ba。看起来替代品(.)(.)(没有的替代品$)在两种替换中都成功了,无论其位置是第一个替代品还是第二个替代品。为什么会这样?这种替代方案的决胜局是什么?


正则表达式的 POSIX 规范指定1当备选方案从不同位置开始时(在这种情况下优先选择较早的位置),以及当它们从相同位置开始但具有不同长度(优先选择较长的位置)时,决胜局,但是当两个备选方案从相同位置开始并具有相同长度时,它似乎没有指定捕获组的行为,因此将其留给具体实现。

匹配序列的搜索从字符串的开头开始,并在找到与表达式匹配的第一个序列时停止,其中“first”定义为“在字符串中最早开始”。如果模式允许可变数量的匹配字符,因此从该点开始有多个这样的序列,则匹配最长的这样的序列。[...] – The Open Group Base Specifications 第 7 期,2018 年版

这是该现象的一个运行示例。

echo ab|sed -r 's/(.)(.)|(.)(.)$/\2\1\3\4/'
echo ab|sed -r 's/(.)(.)$|(.)(.)/\1\2\4\3/'

在线尝试!

4

1 回答 1

1

假设一个答案。

给定输入 line ab,两者(.)(.)(.)(.)$都将匹配ab 相同的长度2。因此,正如您在问题中所说,两个正则表达式从相同的起点匹配相同的长度。

但是,我想说的是,在与(.)(.)匹配的那一刻ab,引擎将再做一次检查(针对$),以检查是否(.)(.)$也匹配(即它是否在 EOL 匹配),在这种情况下,后一个正则表达式不会无论如何都是首选,因为它具有相同的长度并且与已经匹配的前正则表达式在同一点开始。所以对我来说,引擎将引用返回到(.)(.)without中的组是有意义的$

我认为我的这种推理意味着匹配对可打印字符是贪婪的,但对不可打印字符是懒惰的。

与此比较/对比:

echo ab|sed -r 's/^(.)(.)|(.)(.)/\2\1\3\4/'
ba
echo ab|sed -r 's/(.)(.)|^(.)(.)/\2\1\3\4/'
ba

其中最左边的正则表达式在这两种情况下都匹配。

于 2020-02-01T16:52:23.250 回答