除了指定的字符串外,匹配所有内容

221

我知道以下正则表达式可以匹配"red", "green", 或者 "blue"。

red|green|blue

有没有一种简单的方法,可以使其匹配除了几个指定字符串之外的所有内容?


3
并非所有类型的正则表达式都能做到这一点。你在哪个开发环境中工作?Java?Perl?.NET?一些 C/C++ 正则表达式库?还是关系型数据库管理系统? - FrustratedWithFormsDesigner
8
你没有说明你需要它用于什么,但你可以简单地反转“match”操作的意义。如果你想要在非匹配的部分进行提取,这种方法对你没有帮助,但如果你想测试一个被排除的字符串是否不存在,这种方法就可行:if (!s.match(/red|green|blue/)) ... 注意:我知道原文没有说明使用哪种语言/框架,所以上述内容应该被视为一般性示例,而不是规定性的。 - tvanfosson
7个回答

255
如果您想确保字符串既不是红色、绿色或蓝色,那么caskey的答案就是它。但通常想要的是确保该行中任何位置都不包含红、绿或蓝。为此,请使用正则表达式的锚点 ^ 并在负向前瞻中包含.*
^(?!.*(red|green|blue))

另外,假设你想要包含单词“engine”的行,但没有这些颜色:

^(?!.*(red|green|blue)).*engine

你可能认为可以将.*因子提到正则表达式的头部:

^.*(?!red|green|blue)engine     # Does not work

但你不可以。 你必须同时拥有两个.*实例才能使它起作用。


3
重要的是要说,前瞻不是BRE(基本)或ERE(扩展)正则表达式的一部分。您需要PCRE(Perl兼容)或类似工具。你最后的例子无法工作,因为贪婪的.*在开头会匹配任何内容。您需要使初始匹配更加具体。例如,只匹配空格:printf 'redengine\nblackengine\ngreenengine\n' | grep -P '^\s*(?!red|green|blue)\w*engine' - pabouk - Ukraine stay strong

84

这取决于编程语言,但通常可以使用否定断言来实现,例如:

(?!red|green|blue)

(感谢语法修复,上述代码对于Java和Perl有效,但是结果可能因环境而异)


4
@caskey,完整的答案是由我的和你的结合而成。如果你想把它们合并在一起,我会删除我的回答。 - Wayne Conrad
38
如果您解释一下,这个答案会更加有用。例如: "?" 和 "!" 表示什么?为什么需要捕获组? - Lii
这也是有效的Python代码。 - Joe Mornin
1
只是使用 Delphi 的 regEx 库,并且它只能像这样工作:^(?!red|green|blue)。在 https://regex101.com/ 上测试结果也是一样的。那么上面的代码是缺少了 ^,还是 Java/Perl/Python 真的可以这样工作呢? - Peter
2
我对@Lii的评论回答很好奇。 - scarface
@Lii,这是一种环视表达式,它在完成后不会改变正则表达式的当前整体评估位置。这个是负向前瞻,写作(?!,意思是,“从我的正则表达式的当前位置开始,接下来的文本不是'红色'、'绿色'或'蓝色'吗?”还有正向前瞻,以及正向和负向后顾,其中评估是从当前位置向后看的。关键词是“看”,但不“移动”。 - undefined

68

匹配除指定字符串之外的任何内容

如果您想匹配整个字符串,但希望排除某些特定字符串,可以按如下方式执行:

^(?!(red|green|blue)$).*$

这表示从字符串开头开始匹配,其中不能以红色、绿色或蓝色开头和结尾,并且匹配其他任何内容到字符串的结尾。

您可以在此处尝试:https://regex101.com/r/rMbYHz/2

请注意,这仅适用于支持负向先行断言的正则表达式引擎。


是的!这是正确答案,因为它不会匹配包含特定字符串的单词。 - Aaronius

43

不需要使用负向预查。这里有一个可行的示例:

/([\s\S]*?)(red|green|blue|)/g

描述:

  • [\s\S] - 匹配任何字符
  • * - 从前一个组匹配0到无限个
  • ? - 尽可能少地匹配
  • (red|green|blue|) - 匹配其中之一或什么也不匹配
  • g - 重复模式

示例:

whiteredwhiteredgreenbluewhiteredgreenbluewhiteredgreenbluewhiteredgreenbluewhiteredgreenbluewhiteredgreenbluewhiteredgreenbluewhiteredwhiteredwhiteredwhiteredwhiteredwhiteredgreenbluewhiteredwhiteredwhiteredwhiteredwhiteredredgreenredgreenredgreenredgreenredgreenbluewhiteredbluewhiteredbluewhiteredbluewhiteredbluewhiteredwhite

将是:

whitewhitewhitewhitewhitewhitewhitewhitewhitewhitewhitewhitewhitewhitewhitewhitewhitewhitewhitewhitewhitewhitewhitewhite

测试:regex101.com


8
用"."替换[\s\S]可以大大减少步骤。一开始我很困惑,为什么其他的示例好像都是单独捕获每个单词。虽然这种方式需要稍微多一些正则表达式步骤,但需要进行的后处理工作要少得多。 - Zatronium
5
但是这并不进行匹配(文本验证),它只是在替换过程中删除指定的文本。 - Marek R
这个解决方案不会输出已知单词后的最终文本块。因此,没有必要比较速度,这是错误的。 - Wiktor Stribiżew
1
@WiktorStribiżew 已修复。 - Artem P
这个问题是如何将正则语言的补集操作符翻译成实际可用的PCRE。这个答案涉及到特定模式的捕获组,与问题无关。 - Dawid Toton

18
我有同样的问题,提出的解决方案几乎都能工作,但它们都有一些问题。最后我使用的正则表达式是:
^(?!red|green|blue).*

我在Javascript和.NET中进行了测试。 .*不应该放在负向前瞻中,像这样:^(?!.*red|green|blue),否则它会使第一个元素的行为与其他元素不同(即"anotherred"不会匹配,而"anothergreen"会匹配)。

^(?!.*red|green|blue) 会匹配所有以 .*redgreenblue 开头不同的内容。"anotherred" 在开头匹配了 .*red,所以被排除了。"anothergreen" 在开头没有匹配到 green,所以被接受了。^(?!.*(red|green|blue)) 可以用来排除 "anotherred" 和 "anothergreen"。 - Carlos Eugenio Thompson Pinzón

12

通常使用正则表达式模式来匹配除了与该模式匹配的文本以外的任何文本,这通常通过使用正则表达式模式拆分字符串来实现。

示例

  • - Regex.Split(text, @"red|green|blue") 或者,要去除空值,Regex.Split(text, @"red|green|blue").Where(x => !string.IsNullOrEmpty(x)) (参见演示)
  • - Regex.Split(text, "red|green|blue") 或者,要移除空项,Regex.Split(text, "red|green|blue").Where(Function(s) Not String.IsNullOrWhitespace(s)) (参见演示,或者此演示支持LINQ)
  • - text.split(/red|green|blue/) (这里不需要使用g修饰符!) (要去除空值,请使用text.split(/red|green|blue/).filter(Boolean)),参见演示
  • - text.split("red|green|blue"),或者- 保留所有尾随空项- 使用text.split("red|green|blue", -1),或者使用更多代码来移除它们移除所有空项 (参见演示)
  • - 类似于Java,text.split(/red|green|blue/),要获取所有尾随项,请使用text.split(/red|green|blue/, -1),要移除所有空项,请使用text.split(/red|green|blue/).findAll {it != ""}) (参见演示)
  • - text.split(Regex("red|green|blue")) 或者,要移除空白项,请使用text.split(Regex("red|green|blue")).filter{ !it.isBlank() },参见演示
  • - text.split("red|green|blue"),或者保留所有尾随空项,请使用text.split("red|green|blue", -1),要移除所有空项,请使用text.split("red|green|blue").filter(_.nonEmpty),参见演示
  • - text.split(/red|green|blue/),要去除空值,请使用.split(/red|green|blue/).reject(&:empty?) (要获取前导和尾随空项,请使用-1作为第二个参数,.split(/red|green|blue/, -1)) (参见演示)
  • - my @result1 = split /red|green|blue/, $text;,或者带有所有尾随空项,my @result2 = split /red|green|blue/, $text, -1;,或者没有任何空项,my @result3 = grep { /\S/ } split /red|green|blue/, $text; (参见演示)
  • - preg_split('~red|green|blue~', $text) 或者 preg_split('~red|green|blue~', $text, -1, PREG_SPLIT_NO_EMPTY)以不输出空项 (参见演示)
  • - re.split(r'red|green|blue', text) 或者,要移除空项,请使用list(filter(None, re.split(r'red|green|blue', text))) (参见演示)
  • - 使用注意:如果您的模式包含捕获组, 正则表达式分割函数/方法可能会有不同的行为,这还取决于其他选项。请参考相应的分割方法文档。

-1

除了单词“red”之外的所有内容

var href = '(text-1) (red) (text-3) (text-4) (text-5)';

var test = href.replace(/\((\b(?!red\b)[\s\S]*?)\)/g, testF); 

function testF(match, p1, p2, offset, str_full) {
  p1 = "-"+p1+"-";
  return p1;
}

console.log(test);

除了单词“红色”之外的所有内容

var href = '(text-1) (frede) (text-3) (text-4) (text-5)';

var test = href.replace(/\(([\s\S]*?)\)/g, testF); 

function testF(match, p1, p2, offset, str_full) {
  p1 = p1.replace(/red/g, '');
  p1 = "-"+p1+"-";
  return p1;
}

console.log(test);


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