到目前为止给出的所有答案对我来说都是错误的,因为它们进行了贪婪的匹配。在出现多次“否”的行中,它们将匹配最后一个:
YES YES YES YES no YES YES no YES
^^^^^^^^^^^^^^^^^^^^^^^^^^^
但是,将所有内容匹配到某个字符串的目的通常是在第一次出现时停止:
YES YES YES YES no YES YES no YES
^^^^^^^^^^^^^^^^
为此,请使用非贪婪正则表达式,例如:
.\{-}\(no\)\@=
\{-}
是乘数的非贪心替代方案*
(见:help non-greedy
)。\@=
是一个积极的前瞻,它将检查“否”是否跟随但不会将其包含在匹配中(请参阅:help /\@=
)。
作为旁注,“将所有内容匹配到某个字符串”的常见场景是匹配带有开始和结束分隔符的表达式。例子:
- C 风格的字符串文字
"string literal"
;
- C++ 风格的注释
// comment\n
(其结束分隔符是换行符);
- 正则表达式
/regex/
。
在许多这样的情况下,关闭分隔符实际上可能在要匹配的表达式中出现转义。例如:
- AC 风格的字符串文字可能包含双引号字符,在这种情况下,它必须像
\"
.
- 在 C++ 注释中,前面有反斜杠的换行符将被忽略,其副作用是注释在下一行继续。
- 正则表达式可能包含斜杠;在对正则表达式语法的一种过于简单化的方法中,我们可以假设所有这些出现都被转义为
\/
(这是不正确的,例如,/[abc/]/
它也是一个有效的 Vim 正则表达式,它匹配“a”、“b”中的任何字符, “C”, ”/”)。
因此,我们必须改进我们的正则表达式,使其不会在结束分隔符的转义出现处停止。
让我们从错误的正则表达式开始,它匹配起始分隔符“start”和结束分隔符“stop”之间的任何内容(由于正向后视\@<=
和正向前瞻,它们不包含在匹配中\@=
):
\(start\)\@<=\_.\{-}\(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
^^^^^^^^^^^^^^^^^^^