Perl:不包含PATTERN的匹配字符串

时间:2014-05-01 07:04:25

标签: regex perl

使用Perl正则表达式将字符串切成可用的部分时,我需要匹配除某个模式之外的所有内容。我在Perl Monks找到了这个提示后解决了它:

/^(?:(?!PATTERN).)*$/;    # Matches strings not containing PATTERN

虽然我解决了我最初的问题,但我对它的实际工作方式一无所知。我检查了perlre,但它有点过于正式无法掌握。

Regular expression to match a line that doesn't contain a word?对理解有很大帮助,但为什么我的示例中的.?:以及外括号如何工作?

有人可以打破正则表达式并用简单的词语解释它是如何工作的吗?

2 个答案:

答案 0 :(得分:9)

逐个构建它(并且在整个假设字符串或PATTERN中没有换行符):

匹配任何字符串:

/^.*$/

但我们不希望.匹配开始PATTERN的字符,因此请替换

.

(?!PATTERN).

这使用负向前瞻,测试给定模式而不实际消耗任何字符串,只有在模式在字符串中的给定点不匹配时才成功。所以就像说:

if PATTERN doesn't match at this point,
    match the next character

这需要对字符串中的每个字符进行,因此*用于匹配从字符串的开头到结尾的零次或多次。

要使*适用于否定前瞻和.的组合,而不仅仅是.,它需要被括号括起来,因为没有理由要捕获,它们应该是非捕获括号(?: )

(?:(?!PATTERN).)*

然后放回锚点以确保我们测试字符串中的每个位置:

/^(?:(?!PATTERN).)*$/

请注意,此解决方案作为较大匹配的一部分特别有用;例如匹配任何字符串foo以及之后baz但之间没有bar

/foo(?:(?!bar).)*baz/

如果没有这些考虑因素,您可以这样做:

/^(?!.*PATTERN)/

检查PATTERN与字符串中的任何位置都不匹配。

关于换行符:正则表达式和换行符存在两个问题。首先,.与换行符不匹配,因此"foo\nbar" =~ /^(?:(?!baz).)*$/不匹配,即使该字符串不包含baz。您需要添加/ s标志以使.匹配任何字符; "foo\nbar" =~ /^(?:(?!baz).)*$/s正确匹配。其次,$仅在字符串的末尾不匹配,它也可以在字符串末尾的换行符之前匹配。所以"foo\n" =~ /^(?:(?!\s).)*$/s确实匹配,即使字符串包含空格而你试图只匹配没有空格的字符串; \z始终只在最后匹配,因此"foo\n" =~ /^(?:(?!\s).)*\z/s无法正确匹配实际包含\s的字符串。所以正确的通用正则表达式是/^(?:(?!PATTERN).)*\z/s

答案 1 :(得分:3)

jippie,首先,这是一个提示。如果您看到一个对您来说不是很明显的正则表达式,您可以将其转储到解释每个标记的工具中。

例如,这是RegexBuddy输出:

"
^                # Assert position at the beginning of a line (at beginning of the string or after a line break character) (line feed)
(?:              # Match the regular expression below
   (?!              # Assert that it is impossible to match the regex below starting at this position (negative lookahead)
      PATTERN          # Match the character string “PATTERN” literally (case insensitive)
   )
   .                # Match any single character that is NOT a line break character (line feed)
)
   *                # Between zero and unlimited times, as many times as possible, giving back as needed (greedy)
\$                # Assert position at the end of a line (at the end of the string or before a line break character) (line feed)
                    # Perl 5.18 allows a zero-length match at the position where the previous match ends.
                    # Perl 5.18 attempts the next match at the same position as the previous match if it was zero-length and may find a non-zero-length match at the same position.
"

有些人也使用regex101。

人性化解释

现在,如果我必须解释正则表达式,我就不会那么线性。我首先要说它完全由^$锚定,这意味着唯一可能的匹配是整个字符串,而不是该字符串的子字符串。

然后我们来到肉:由(?:引入的非捕获组,并由*

重复多次

这个小组做了什么?它包含

  1. 否定前瞻(你可能希望read up on lookarounds here)断言在字符串中的这个确切位置,我们无法匹配单词PATTERN,
  2. 然后是一个与下一个字符匹配的点
  3. 这意味着在字符串中的每个位置,我们断言我们无法匹配PATTERN,然后我们匹配下一个字符。

    如果PATTERN可以在任何地方匹配,则负向前瞻失败,整个正则表达式也是如此。