【问题标题】:How can I repeatedly match from A until B in VIM?如何在 VIM 中从 A 到 B 重复匹配?
【发布时间】:2009-04-10 00:41:28
【问题描述】:

我需要获取<Annotation></Annotation> 之间的所有文本,其中出现了单词MATCH。如何在 VIM 中做到这一点?

<Annotation about="MATCH UNTIL </Annotation>   " timestamp="0x000463e92263dd4a" href="     5raS5maS90ZWh0YXZha29rb2VsbWEvbGFza2FyaS8QyrqPk5L9mAI">                                                                        
  <Label name="las" />
  <Label name="_cse_6sbbohxmd_c" />
  <AdditionalData attribute="original_url" value="MATCH UNTIL </Annotation>       " />
</Annotation>
<Annotation about="NO MATCH" href="     Cjl3aWtpLmhlbHNpbmtpLmZpL2Rpc3BsYXkvbWF0aHN0YXRLdXJzc2l0L0thaWtraStrdXJzc2l0LyoQh_HGoJH9mAI">
  <Label name="_cse_6sbbohxmd_c" />
  <Label name="courses" />
  <Label name="kurssit" />
  <AdditionalData attribute="original_url" value="NO MATCH" />
</Annotation>
<Annotation about="MATCH UNTIL </ANNOTATION>     " score="1" timestamp="0x000463e90f8eed5c" href="CiZtYXRoc3RhdC5oZWx     zaW5raS5maS90ZWh0YXZha29rb2VsbWEvKhDc2rv8kP2YAg">
  <Label name="_cse_6sbbohxmd_c" />
  <Label name="exercises_without_solutions" />
  <Label name="tehtäväkokoelma" />
  <AdditionalData attribute="original_url" value="MATCH UNTIL </ANNOTATION>" />
</Annotation>

【问题讨论】:

  • 我假设您想以理解 XML 的方式执行此操作,因此不是元素结尾的 的出现不被 VIM 匹配,但被计为匹配体的一部分?如果是这样,我不知道VIM是否可以像这样进行XML-aware模式匹配。
  • 另外,我尝试使示例的格式更易于查看。我假设属性中的空格是有目的的,并留在那里。
  • “获取所有文本”是什么意思?突出它?抽吗?将其附加到命名缓冲区?
  • jholloway7:我不知道如何将其附加到命名缓冲区并将突出显示的文本保存到单独的文件中。
  • 您的 XML 无效。属性值中不允许使用 &lt;

标签: vim design-patterns match


【解决方案1】:

首先,免责声明:任何使用正则表达式对 XML 进行切片和切块的尝试都是脆弱的;真正的 XML 解析器会做得更好。

图案:

\(<Annotation\(\s*\w\+="[^"]\{-}"\s\{-}\)*>\)\@<=\(\(<\/Annotation\)\@!\_.\)\{-}"MATCH\_.\{-}\(<\/Annotation>\)\@=

让我们分解一下......

第 1 组是 &lt;Annotation\(\s*\w\+="[^"]\{-}"\s\{-}\)*&gt;。它与 Attribute 元素的起始标签匹配。 Group 2,嵌入在 Group 1 中,匹配一个属性,可以重复 0 次或多次。

第 2 组是 \s*\w\+="[^"]\{-}"\s\{-}。这些作品中的大多数都是常用的;最不寻常的是\{-},这意味着非贪婪重复(*? 在 Perl 兼容的正则表达式中)。最后的非贪心空白匹配对性能很重要;没有它,Vim 将尝试所有可能的方法来分割第 2 组末尾的 \s* 和下一次出现第 2 组开头的 \s* 之间的属性之间的空白。

第 1 组后面是 \@&lt;=。这是一个零宽度的正向回顾。它可以防止开始标签包含在匹配的文本中(例如,对于 s///)。

第 3 组是 \(&lt;\/Annotation\)\@!\_.。它包括第 4 组,它与属性结束标记的开头相匹配。 \@! 是零宽度负前瞻,\_. 匹配任何字符(包括换行符)。这些组一起匹配任何字符,除了属性结束标记开始的位置。第 3 组后面是一个非贪婪重复标记 \{-},以便它匹配 MATCH 之前的最小文本块。如果您要使用\_. 而不是第 3 组,则匹配的文本可能包含一个注释元素的结束标记,该元素包含 MATCH,并继续使用 MATCH 进入下一个 Annotation 元素。 (试试看。)

下一点很简单:在结束标记之前找到 MATCH 和最少数量的其他字符。

第 5 组很简单:它是结束标记。 \@= 是一个零宽度的正向前瞻,这里包含它的原因与开始标签的 \@&lt;= 相同。我们必须重复&lt;\/Attribute 而不是使用\4,因为没有捕获具有零宽度修饰符的组。

【讨论】:

    【解决方案2】:

    必须在 vim 中完成吗?你可以作弊,然后打开第二个窗口,在其中将某些内容输入更多/更少,告诉你在 vim 中要转到哪个行号?

    -- 编辑--

    我从未在 vi[m] 中进行过多行匹配/搜索。但是,在另一个窗口中作弊:

    perl -n -e 'if ( /<tag/ .. /<\/tag/)' -e '{ print "$.:$_"; }' file.xml | less
    

    将显示“标签”(或其他更长的匹配名称)的元素/块,以及行号,更少,然后您可以在每个块中搜索其他文本。

    够近了吗?

    -- 编辑--

    在“less”中输入

    /MATCH
    

    搜索 MATCH 的出现。左边距是该实例(在目标元素/标签内)所在的行号。

    在 vi[m] 中,键入

    :n
    

    其中“n”是所需的行号。

    当然,如果您真正想做的是某种搜索/提取/替换,那就更复杂了。那时,awk / perl / ruby​​(或符合您口味的类似东西......或 xsl?)确实是您应该用于转换的工具。

    【讨论】:

    • 我认为这样的事情将是唯一可能的答案,因为要做到这一点,您需要使用 XML 解析器。
    • 匹配词应该在哪里?代替..?
    猜你喜欢
    • 1970-01-01
    • 2016-02-23
    • 1970-01-01
    • 2013-05-26
    • 2020-05-26
    • 1970-01-01
    • 2011-06-23
    • 2014-09-22
    • 1970-01-01
    相关资源
    最近更新 更多