Vim / 搜索模式中的 sed 正则表达式反向引用

Vim / sed regex backreference in search pattern

Vim 帮助说:

      Matches the same string that was matched by     */* *E65*
        the first sub-expression in \( and \). {not in Vi}
        Example: "\([a-z]\)." matches "ata", "ehe", "tot", etc. 

看起来 backreference 可以用于搜索模式。我开始玩它,我注意到我无法解释的行为。这是我的文件:

<paper-input label="Input label"> Some text </paper-input>
<paper-input label="Input label"> Some text </paper-inputa>
<aza> Some text </az>
<az> Some text </az>
<az> Some text </aza>

我想匹配开始和结束标签匹配的行,即:

<paper-input label="Input label"> Some text </paper-input>
<az> Some text </az>

我的测试正则表达式是:

%s,<\([^ >]\+\).*<\/>,,gn

但这匹配行:134。同样的事情 sed:

$ sed -ne 's,<\([^ >]\+\).*<\/>,[=15=],p' file
<paper-input label="Input label"> Some text </paper-input>
<aza> Some text </az>
<az> Some text </az>

这:<\([^ >]\+\) 应该是贪心的,当试图在最后没有 </code> 的情况下匹配它时,所有组都是正确的。但是当我添加 <code> 时,<\([^ >]\+\) 似乎变得不贪心,它试图在 第 3 行 中强制匹配。有人可以解释为什么它匹配 3rd line:

<aza> Some text </az>

这也是一个regex101 demo

注意 这与正则表达式本身无关(可能还有其他方法),而是与正则表达式的行为有关。

目前,第 3 行 (<aza>) 显示为匹配项的原因是正则表达式中的 .* 项可以匹配多行。所以第 3 行匹配,因为第 5 行有结束标记。要更正此问题,请强制正则表达式仅在 same 行上找到匹配的结束标记:

%s,<\([^ >]\+\)[^\n]*?<\/>,,gn
               ^^^^^ use [^\n]* instead of .*

您需要添加 \> 以指示 单词结束 。可能还有其他具有 0 宽度模式的解决方案,但这会使事情复杂化。

此外,您的分隔符是 ,,而不是 /

给出:

%s,<\([^ >]\+\)\>.*</>,,gn

要了解您的正则表达式为何如此运行,您需要了解回溯正则表达式引擎的作用。

引擎会贪婪地匹配和消耗尽可能多的字符。但是,如果它没有找到匹配项,它会返回并尝试找到仍然满足该模式的不同匹配项。

%s,<\([^ >]\+\).*<\/>,,gn

第三行<aza> Some text </az>,

正则表达式引擎查看 = aza。并查看 .*</aza> 是否与字符串的其余部分匹配。它没有,所以它为 </code> 选择了其他东西。下次它选择 <code> = az 并查看 .*</az> 是否与字符串的其余部分匹配,并且确实如此。所以字符串匹配

(这是一个简化版本。我跳过了 .* 本身可能会做很多回溯的事实)


解决它就像在正则表达式中添加一个锚点阻止正则表达式搜索可以满足 </code> 的其他值一样简单。在这种情况下,匹配 space 或 <code>> 就足够了。