到目前为止给出的所有答案对我来说都是错误的,因为它们执行了
贪婪匹配。在一行中有几个“no”的情况下,它们将匹配到最后一个:
YES YES YES YES no YES YES no YES
^^^^^^^^^^^^^^^^^^^^^^^^^^^
然而,当将所有内容与某个字符串进行匹配时,目的通常是在第一次出现时停止:
YES YES YES YES no YES YES no YES
^^^^^^^^^^^^^^^^
为此,使用一个非贪婪正则表达式,例如:
.\{-}\(no\)\@=
\{-}
是非贪婪模式下的乘数 *
的替代品(请参见 :help non-greedy
)。\@=
是正向预查,它将检查是否跟随“no”,但不会将其包含在匹配中(请参见 :help /\@=
)。
作为一个附注,匹配“一直到某个字符串”的常见场景是匹配具有开放和关闭分隔符的表达式。例如:
- C风格的字符串字面值
"string literal"
;
- C++样式的注释
// comment\n
(其结束定界符是换行符);
- 正则表达式
/regex/
。
在许多这种情况下,结束分隔符实际上可能出现在要匹配的表达式中。例如:
- C风格的字符串文字可能包含双引号字符,在这种情况下,它必须以转义形式出现,如
\"
。
- 在C++注释中,由反斜杠前导的换行符将被忽略,副作用是注释继续在以下行上。
- 正则表达式可以包含斜杠;在对正则表达式的语法采取过度简化的方法时,我们可以假设所有这些出现都是转义的,如
\/
(这不是真的,例如/[abc/]/
是一个有效的Vim正则表达式,它匹配“a”、“b”、“c”之间的任何字符,“/”)。
因此,我们必须优化我们的正则表达式,以便它不会在转义的结束定界符上停止匹配。
让我们从错误的正则表达式开始,它匹配起始定界符“start”和结束定界符“stop”之间的任何内容(由于正向后查找
\@<=
和正向前查找
\@=
,它们不包括在匹配中):
\(start\)\@<=\_.\{-}\(stop\)\@=
比赛将在任何出现“stop”的情况下停止,即使它被转义。
test start test \stop test stop test
^^^^^^^
为了解决这个问题,我们可以将匹配任何字符(包括换行符)的
\_.
替换为
\_[^\\]
的分支(它匹配任何字符
但不是反斜杠)和
\\\_.
(它匹配一个反斜杠后面跟着任何字符)。这意味着任何未转义的反斜杠都将被解释为转义序列的开始。反斜杠本身可以被转义,因此
\\stop
是一个转义的反斜杠,后面跟着一个真正的结束定界符。
这是布莱叶表达式:
\(start\)\@<=\(\_[^\\]\|\\\_.\)\{-}\(stop\)\@=
还有一些测试:
test start test \stop test stop test
^^^^^^^^^^^^^^^^^
test start test \\stop test stop test
^^^^^^^^
test start test \\\stop test stop test
^^^^^^^^^^^^^^^^^^^
n
。但是我希望它在“no.”之前停止。 - Mihai Nagyn
之前的高亮显示,您可以在@MihaiNagy处使用.+\zeno
。 - Kent