"AND NOT"操作的正则表达式

37
我正在寻找一般的正则表达式构造,以匹配模式x中除模式y匹配之外的所有内容。这很难完全而简明地解释......请参见Material Nonimplication了解正式定义。
例如,匹配任何单词字符(\w)除了'p'。请注意,我正在从一个更大的集合(所有单词字符)中减去一个小集合(字母“ p”)。我不能仅仅说[^p],因为它没有考虑到限制更大的集合只有单词字符。对于这个小例子,当然,我可以手动重构类似于[a-oq-zA-OQ-Z0-9_]的东西,这是痛苦但可行的。但是我正在寻找一个更通用的结构,因此至少可以更复杂地表示大的正数集。例如,在不以“My”开头时匹配((?<=(so|me|^))big(com?pl{1,3}ex([pA]t{2}ern),除非以“My”开头。 编辑:我意识到那是一个糟糕的例子,因为在开头或结尾排除某些内容是负向前瞻和后瞻表达式可行的情况。(Bohemian,我仍然为你的说明点赞)。因此......如何排除包含“My”在中间的匹配?...我仍然真正寻找一个通用结构,就像以下伪SQL的正则表达式等效物。
select [captures] from [input]
where (
    input MATCHES [pattern1]
    AND NOT capture MATCHES [pattern2]
)

如果答案是“它不存在,原因是……”,我也想知道。

编辑2:如果我想定义自己的函数来完成这个任务,它可能是这样的(这是一个C# LINQ版本):

public static Match[] RegexMNI(string input, 
                               string positivePattern, 
                               string negativePattern) {
    return (from Match m in Regex.Matches(input, positivePattern)
            where !Regex.IsMatch(m.Value, negativePattern)
            select m).ToArray();
}

我仍然在想是否有本地的正则表达式结构可以实现此功能。

也许你可以接受一个答案... - Bohemian
2
@Bohemian 没有人真正回答了这个问题。他们都卡在了我的例子的具体细节上,而没有在抽象但完整的问题上回答。两个编辑都清楚地提供了集合逻辑概念。 - Joshua Honig
4
回答您编辑后的问题,"包含A但不包含B"的一般解决方案是 ^(?!.*B).*A - Bohemian
?<=是什么?虽然我主要使用的是JavaScript,其正则表达式语言不太具有表现力,但我以前从未见过这个表达式。 - trysis
@Bohemian 我认为是 ^(?!.*B).*A.* 选择整行 - Yuri Aps
4个回答

28

这将匹配任何一个单词字符,但不是一个 p 标签。

((?=[^p])\w)

为了解决您的问题,在输入中使用一个否定前瞻来匹配任何地方不包含"My",即(?!.*My)
^(?!.*My)((?<=(so|me|^))big(com?pl{1,3}ex([pA]t{2}ern)

请注意,开始输入的锚点^是必需的,以使其正常工作。

已编辑以将负向前瞻更改为断言“我的”不会出现在输入的任何位置(先前仅检查了“我的”是否在开头)。 - Bohemian
楼主明确表示,他希望在找到的“匹配”表达式中不包含“My”。您的负向预查搜索整个字符串输入而不是子集。实际上,他想要将一个正则表达式传递给另一个正则表达式,据我所知,这似乎没有办法在没有脚本的情况下实现。对于如何在不使用脚本或使预查与主要正则表达式模式一样复杂的情况下解决此问题,您有何想法? - horta

16

我想知道为什么人们要在一个大而复杂的正则表达式中尝试做复杂的事情?

为什么不能将问题分解成子部分,然后制作非常简单的正则表达式来逐个匹配它们呢?在这种情况下,首先匹配\w,然后如果第一个匹配成功,则匹配[^p]。Perl(和其他语言)允许构建看起来非常复杂的正则表达式,使您能够在一个大而笨重的正则表达式中完成所需的操作(或者,正如可能是的那样,使用简短而精练的加密-regex),但是为了需要读取(和维护!)代码的人着想,您需要完全记录它。因此最好从一开始就使其易于理解。

抱歉,发牢骚结束。


3
啊,是的,Zawinski效应,即使用正则表达式会增加问题数量。(我最喜欢的一个例子是有人要求编写一个正则表达式来接受XML文档中已写入的有效IEEE双精度数值......) - Donal Fellows
我想一次性匹配的原因是我想在输入字符串中捕获和操作多个匹配项,例如在几百行代码中查找和重新格式化符合某种模式的声明。我可以完全放弃正则表达式,逐个字符解析...但如果有好的强大工具,为什么不使用呢! - Joshua Honig
编程语言是一种与正则表达式识别的递归可枚举语法不同类别的语法(上下文无关),因此要小心... - escape-llc

7

在您的编辑之后,它仍然是负向先行断言,但加了一个额外的量词。

如果您想确保整个字符串不包含“My”,则可以这样做:

(?!.*My)^.*$

在Regexr上查看此处

这将匹配任何字符序列(末尾的.*),并且开始处的(?!.*My).*将在字符串中出现"My"时失败。

如果您想要匹配不完全是"My"的任何内容,请使用锚点。

(?!^My$).*

1
在查看了正则表达式的这些主题之后:前瞻、后顾、嵌套、AND运算符、递归、子程序、条件、锚点和组,我得出结论:没有解决方案能够满足你的要求。
前瞻无法解决这个相对简单的情况的原因是:

三个单词,不包含 My 作为一个单词。

正则表达式:

^(?!.*My.*)(\b\w+\b\s\b\w+\b\s\b\w+\b)

匹配:

作为其中之一的包括

前三个单词无法匹配,因为"My"在它们之后。如果"My"在整个字符串的末尾,你将永远无法匹配任何东西,因为每个前瞻都会失败,因为它们都会看到这一点。

问题似乎是虽然前瞻有一个隐含的锚定点来确定它开始匹配的位置,但没有办法根据RegEx的另一部分的结果终止前瞻结束其搜索的锚定点。这意味着你必须在负向前瞻中复制所有的RegEx,以手动创建你所需的锚定点。

这很令人沮丧和痛苦。"解决方案"似乎是使用脚本语言执行两个正则表达式。一个在另一个上面。我惊讶于这种功能不是更好地内置到正则表达式引擎中。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接