使用Perl正则表达式将字符串切成可用的部分时,我需要匹配除某个模式之外的所有内容。我在Perl Monks找到了这个提示后解决了它:
/^(?:(?!PATTERN).)*$/; # Matches strings not containing PATTERN
虽然我解决了我最初的问题,但我对它的实际工作方式一无所知。我检查了perlre,但它有点过于正式无法掌握。
Regular expression to match a line that doesn't contain a word?对理解有很大帮助,但为什么我的示例中的.
和?:
以及外括号如何工作?
有人可以打破正则表达式并用简单的词语解释它是如何工作的吗?
答案 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。
人性化解释
现在,如果我必须解释正则表达式,我就不会那么线性。我首先要说它完全由^
和$
锚定,这意味着唯一可能的匹配是整个字符串,而不是该字符串的子字符串。
然后我们来到肉:由(?:
引入的非捕获组,并由*
这个小组做了什么?它包含
这意味着在字符串中的每个位置,我们断言我们无法匹配PATTERN,然后我们匹配下一个字符。
如果PATTERN可以在任何地方匹配,则负向前瞻失败,整个正则表达式也是如此。