【问题标题】:Vim / sed regex backreference in search pattern搜索模式中的 Vim / sed 正则表达式反向引用
【发布时间】:2017-01-15 19:08:44
【问题描述】:

Vim 帮助说:

\1      Matches the same string that was matched by     */\1* *E65*
        the first sub-expression in \( and \). {not in Vi}
        Example: "\([a-z]\).\1" 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,<\([^ >]\+\).*<\/\1>,,gn

但这匹配行:134sed 也一样:

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

这:&lt;\([^ &gt;]\+\) 应该是贪婪的,当试图匹配它时没有 \1 最后那么所有组都是正确的。但是当我添加\1 时,&lt;\([^ &gt;]\+\) 似乎变得不贪心,它试图在第 3 行中强制匹配。有人可以解释为什么它匹配3rd 行:

<aza> Some text </az>

这也是regex101 demo

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

【问题讨论】:

  • 你应该看看回溯引擎。如果找不到匹配项,则引擎将回溯,直到选择不同的内容。例如\1 在所有回溯之后在第三行等于az。 (因为您从未添加过锚点)
  • 要添加到@FDinoff 的观点,您可以添加规则以匹配空格或> 作为锚点...&lt;\([^ &gt;]\+\)[ &gt;].*&lt;\/\1&gt;
  • @FDinoff 这很有趣。我不知道。
  • @spasic 是的,我了解回溯的工作原理以及空间锚点和 > 似乎是这里最好的主意。
  • @FDinoff 如果您将此添加为答案,我会标记。

标签: regex vim sed backreference capturing-group


【解决方案1】:

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

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

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

对于第三行&lt;aza&gt; Some text &lt;/az&gt;

正则表达式引擎查看\1 = aza。并查看.*&lt;/aza&gt; 是否与字符串的其余部分匹配。它没有,所以它为\1 选择了别的东西。下次它选择\1 = az 并查看.*&lt;/az&gt; 是否与字符串的其余部分匹配并且它匹配。所以字符串匹配

(这是一个简化的版本。我跳过了.* 本身可以进行大量回溯的事实)


解决它就像在正则表达式中添加一个锚点一样简单,可以阻止正则表达式搜索可以满足\1 的其他值。在这种情况下,匹配空格或&gt; 就足够了。

【讨论】:

  • 这是很好的解释。 @LucHermitte 建议的以\&gt; 结尾的词,即&lt;\([^ &gt;]\+\)\&gt;.*&lt;/\1&gt; 也可以。
【解决方案2】:

您需要添加\&gt; 来表示词尾。可能还有其他具有 0 宽度模式的解决方案,但这会使事情复杂化。

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

这给出了:

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

【讨论】:

  • 这与第一行不匹配。此外,正如我在问题中提到的 - 我想了解为什么我的正则表达式不起作用。
  • 我刚刚检查过。这确实匹配第一行(我刚刚检查了我的 gvim 7.4-2207 和我手头的 vim 7-4-2181)。不过,Regex 101 不能很好地处理它。关于解释,@FDinoff 已经给出了。
  • @DawidGrabowski,这同样适用于 vim 7.3-429。难道是你改变了&amp;isk的定义?
  • 我没有复制正则表达式,而是写了它并犯了一个错误。它正在工作(sed 和 vim)
  • 发生这种情况:)
【解决方案3】:

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

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

【讨论】:

  • 为什么你认为.* 匹配多行?它匹配任何字符除了换行
  • @DawidGrabowski 那么你如何解释第 3 行显示为匹配项?
  • 我不知道。这就是我问这个问题的原因。我知道.* 绝对不匹配新行。我添加了regex101 demo
  • \_. 可以用 vim 匹配换行符。不是.
猜你喜欢
  • 2014-12-28
  • 2012-03-01
  • 2023-03-09
  • 1970-01-01
  • 1970-01-01
  • 2011-04-04
  • 1970-01-01
  • 2021-06-22
  • 1970-01-01
相关资源
最近更新 更多