正则expression式匹配不包含单词的行吗?

我知道可以匹配一个单词,然后使用其他工具(例如grep -v )来反转匹配。 但是,我想知道是否有可能使用正则expression式来匹配包含特定单词(例如hede)的行。

input:

 hoho hihi haha hede 

码:

 grep "<Regex for 'doesn't contain hede'>" input 

期望的输出:

 hoho hihi haha 

正则expression式不支持逆匹配的概念并不完全正确。 你可以通过使用负面的查找来模仿这种行为:

 ^((?!hede).)*$ 

上面的正则expression式匹配任何string,或没有换行符的行, 包含(子)string'hede'。 如前所述,这不是正则expression式在(或应该)是“好”的,但仍然可能的。

如果您还需要匹配换行符,请使用DOT-ALL修饰符 (以下列模式中的拖尾s ):

 /^((?!hede).)*$/s 

或者直接使用它:

 /(?s)^((?!hede).)*$/ 

(其中/.../是正则expression式分隔符,即不是模式的一部分)

如果DOT-ALL修饰符不可用,则可以模拟与字符类[\s\S]相同的行为:

 /^((?!hede)[\s\S])*$/ 

说明

一个string只是一个包含n字符的列表。 每个字符之前和之后都有一个空string。 所以n字符的列表将有n+1空string。 考虑string"ABhedeCD"

  ┌──┬───┬──┬───┬──┬───┬──┬───┬──┬───┬──┬───┬──┬───┬──┬───┬──┐ S = │e1│ A │e2│ B │e3│ h │e4│ e │e5│ d │e6│ e │e7│ C │e8│ D │e9│ └──┴───┴──┴───┴──┴───┴──┴───┴──┴───┴──┴───┴──┴───┴──┴───┴──┘ index 0 1 2 3 4 5 6 7 

e是空的string。 正则expression式(?!hede). outlook未来,看看有没有子"hede"被看到,如果是这样的话(所以别的东西被看到),那么. (点)将匹配除换行符之外的任何字符。 查找也称为零宽度断言,因为它们不消耗任何字符。 他们只是断言/validation一些东西。

所以,在我的例子中,每个空string首先被validation,以查看在字符被消耗之前是否没有"hede" . (点)。 正则expression式(?!hede). 将只做一次,所以它被包裹在一个组中,并重复零次或多次: ((?!hede).)* 。 最后,开始和结束input被锚定以确保整个input被消耗: ^((?!hede).)*$

正如你所看到的,input"ABhedeCD"将失败,因为在e3 ,正则expression式(?!hede)失败(前面有"hede" !)。

请注意,解决scheme “hede” 开头

 ^(?!hede).*$ 

通常比包含 “hede”的解决scheme效率更高:

 ^((?!hede).)*$ 

前者只在inputstring的第一个位置而不是在每个位置检查“hede”。

如果你只是把它用于grep,你可以使用grep -v hede来获取所有不包含hede的行。

ETA哦,重读这个问题, grep -v可能就是你所说的“工具选项”。

 ^((?!hede).)*$ 

说明:

^string的开头

(组和捕获到\ 1(0或更多次(尽可能匹配最多))

(?!outlook未来是否没有:

hede你的string

)预见结束

. 除\ n之外的任何字符

)* 1的结尾(注意:因为您正在使用一个量化器对这个捕获,只有捕获的模式的最后重复将被存储在\ 1)

$在一个可选的\ n之前,并且是string的结尾

给出的答案完全正确,只是一个学术问题:

理论计算机科学的意义上的正则expression式是不可能这样做的。 对他们来说,看起来像这样:

 ^([^h].*$)|(h([^e].*$|$))|(he([^h].*$|$))|(heh([^e].*$|$))|(hehe.+$) 

这只是一个完整的匹配。 做分频比赛会更尴尬。

这里有一个很好的解释 ,为什么不容易否定任意的正则expression式。 但是,我必须同意其他答案:如果这不是一个假设的问题,那么正则expression式在这里不是正确的select。

如果您希望正则expression式testing整个string匹配时才会失败,则以下内容将起作用:

 ^(?!hede$).* 

例如 – 如果你想允许除“foo”(即“foofoo”,“barfoo”和“foobar”将通过,但“foo”将失败)的所有值,使用: ^(?!foo$).*

当然,如果你正在检查确切的平等,在这种情况下更好的一般解决scheme是检查string相等性,即

 myStr !== 'foo' 

如果您需要任何正则expression式function(这里,不区分大小写和范围匹配),您甚至可以将否定置于testing之外

 !/^[af]oo$/i.test(myStr) 

然而,在需要积极的正则expression式testing(可能通过API)的情况下,顶部的正则expression式解决scheme可能会有帮助。

FWIW,因为规则语言(又名理性语言)在互补之下是封闭的,所以总能find否定另一个expression式的正则expression式(aka有理expression式)。 但是没有太多的工具实现这一点

Vcsn支持这个运算符(它表示{c} ,后缀)。

首先定义expression式的types:例如,标签是字母( lal_char ),从a字母到a字母z (在处理互补时定义字母表当然非常重要),并且为每个字词计算的“值”是只是布尔: true这个词被接受, false ,被拒绝。

在Python中:

 In [5]: import vcsn c = vcsn.context('lal_char(az), b') c Out[5]: {a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z} → 𝔹 

那么你input你的表情:

 In [6]: e = c.expression('(hede){c}'); e Out[6]: (hede)^c 

将这个expression式转换为一个自动机:

 In [7]: a = e.automaton(); a 

相应的自动机

最后,把这个自动机转换回一个简单的expression式。

 In [8]: print(a.expression()) \e+h(\e+e(\e+d))+([^h]+h([^e]+e([^d]+d([^e]+e[^]))))[^]* 

其中+通常表示为|\e表示空字,通常写[^] . (任何字符)。 所以,有一点改写()|h(ed?)?|([^h]|h([^e]|e([^d]|d([^e]|e.)))).*

你可以在这里看到这个例子,并在那里尝试Vcsn。

基准

我决定评估一些提出的选项,并比较它们的性能,以及使用一些新的function。 基于.NET Regex引擎的基准testing: http : //regexhero.net/tester/

基准文本:

前7行不应该匹配,因为它们包含search的expression式,而下面的7行应该匹配!

 Regex Hero is a real-time online Silverlight Regular Expression Tester. XRegex Hero is a real-time online Silverlight Regular Expression Tester. Regex HeroRegex HeroRegex HeroRegex HeroRegex Hero is a real-time online Silverlight Regular Expression Tester. Regex Her Regex Her Regex Her Regex Her Regex Her Regex Her Regex Hero is a real-time online Silverlight Regular Expression Tester. Regex Her is a real-time online Silverlight Regular Expression Tester.Regex Hero egex Hero egex Hero egex Hero egex Hero egex Hero egex Hero Regex Hero is a real-time online Silverlight Regular Expression Tester. RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRegex Hero is a real-time online Silverlight Regular Expression Tester. Regex Her egex Hero egex Hero is a real-time online Silverlight Regular Expression Tester. Regex Her is a real-time online Silverlight Regular Expression Tester. Regex Her Regex Her Regex Her Regex Her Regex Her Regex Her is a real-time online Silverlight Regular Expression Tester. Nobody is a real-time online Silverlight Regular Expression Tester. Regex Her o egex Hero Regex Hero Reg ex Hero is a real-time online Silverlight Regular Expression Tester. 

结果:

结果是每秒迭代次数为3次的中位数 – 更大的数字=更好

 01: ^((?!Regex Hero).)*$ 3.914 // Accepted Answer 02: ^(?:(?!Regex Hero).)*$ 5.034 // With Non-Capturing group 03: ^(?>[^R]+|R(?!egex Hero))*$ 6.137 // Lookahead only on the right first letter 04: ^(?>(?:.*?Regex Hero)?)^.*$ 7.426 // Match the word and check if you're still at linestart 05: ^(?(?=.*?Regex Hero)(?#fail)|.*)$ 7.371 // Logic Branch: Find Regex Hero? match nothing, else anything P1: ^(?(?=.*?Regex Hero)(*FAIL)|(*ACCEPT)) ????? // Logic Branch in Perl - Quick FAIL P2: .*?Regex Hero(*COMMIT)(*FAIL)|(*ACCEPT) ????? // Direct COMMIT & FAIL in Perl 

由于.NET不支持动作动词(* FAIL等),我无法testing解决schemeP1和P2。

概要:

我试图testing大多数提出的解决scheme,某些优化是可能的某些单词。 例如,如果searchstring的前两个字母不是相同的,则答案03可以扩展为^(?>[^R]+|R+(?!egex Hero))*$从而导致较小的性能增益。

但总体上最可读和性能最快的解决scheme似乎是05使用条件语句或04与拥有量词。 我认为Perl解决scheme应该更快,更易读。

有了负向前瞻,正则expression式可以匹配不包含特定模式的东西。 这由Bart Kiers回答和解释。 很好的解释!

但是在Bart Kiers的回答中,先行部分将会testing1到4个字符,并且匹配任何单个字符。 我们可以避免这种情况,让先行部分检查整个文本,确保没有“hede”,然后正常部分(。*)可以同时吃掉整个文本。

这是改进的正则expression式:

 /^(?!.*?hede).*$/ 

请注意负向视觉部分中的(*?)惰性量词是可选的,您可以根据数据使用(*)贪婪量词:如果“hede”确实存在,并且在文本的开始部分,则懒惰量词可以加快速度 否则,贪婪的量词会更快。 但是,如果“hede”不存在,那么两者将是平等的。

这里是演示代码 。

欲了解更多关于先行的信息,请查看伟大的文章: 掌握先行和后视 。

另外,请查阅JavaScript正则expression式生成器RegexGen.js ,它有助于构build复杂的正则expression式。 使用RegexGen.js,你可以用更可读的方式构造正则expression式:

 var _ = regexGen; var regex = _( _.startOfLine(), _.anything().notContains( // match anything that not contains: _.anything().lazy(), 'hede' // zero or more chars that followed by 'hede', // ie, anything contains 'hede' ), _.endOfLine() ); 

不是正则expression式,但我发现使用序列greps和pipe道来消除噪音是合乎逻辑和有用的。

例如。 searchapacheconfiguration文件没有所有的评论 –

 grep -v '\#' /opt/lampp/etc/httpd.conf # this gives all the non-comment lines 

 grep -v '\#' /opt/lampp/etc/httpd.conf | grep -i dir 

串行grep的逻辑是(不是注释)和(匹配目录)

有了这个,你可以避免在每个职位上testing一下前瞻:

 /^(?:[^h]+|h++(?!ede))*+$/ 

相当于(对.net):

 /^(?>(?:[^h]+|h+(?!ede))*)$/ 

老答案:

 /^(?>[^h]+|h+(?!ede))*$/ 

以下是我如何做到这一点:

 ^[^h]*(h(?!ede)[^h]*)*$ 

比其他答案准确和高效。 它实现了Friedl的“展开循环”效率技术,并且需要更less的回溯。

如果要匹配一个字符来否定类似于否定字符类的字:

例如,一个string:

 <? $str="aaa bbb4 aaa bbb7"; ?> 

不使用:

 <? preg_match('/aaa[^bbb]+?bbb7/s', $str, $matches); ?> 

使用:

 <? preg_match('/aaa(?:(?!bbb).)+?bbb7/s', $str, $matches); ?> 

注意"(?!bbb)." 既不向后也不向前看,例如:

 "(?=abc)abcde", "(?!abc)abcde" 

前面提到的(?:(?!hede).)*很好,因为它可以被锚定。

 ^(?:(?!hede).)*$ # A line without hede foo(?:(?!hede).)*bar # foo followed by bar, without hede between them 

但在这种情况下,以下就足够了:

 ^(?!.*hede) # A line without hede 

这个简化已经准备好添加“AND”子句:

 ^(?!.*hede)(?=.*foo)(?=.*bar) # A line with foo and bar, but without hede ^(?!.*hede)(?=.*foo).*bar # Same 

OP没有指定或标记post来指示将使用正则expression式的上下文(编程语言,编辑器,工具)。

对我而言,我有时需要在使用Textpad编辑文件时执行此操作。

Textpad支持一些正则expression式,但不支持向前看或向后看,所以需要几个步骤。

如果我想保留所有包含string的行,我会这样做:

1.search/replace整个文件,在包含任何文本的每一行的开头添加一个唯一的“标签”。

  Search string:^(.) Replace string:<@#-unique-#@>\1 Replace-all 

2.删除所有包含stringhede (replacestring为空):

  Search string:<@#-unique-#@>.*hede.*\n Replace string:<nothing> Replace-all 

3.在这一点上,所有其余的行包含stringhede 。 从所有行中删除唯一的“标签”(replacestring为空):

  Search string:<@#-unique-#@> Replace string:<nothing> Replace-all 

现在你有原始文本,包含hede删除的所有行。


如果我正在寻找做别的只有线包含stringhede ,我会这样做:

1.search/replace整个文件,在包含任何文本的每一行的开头添加一个唯一的“标签”。

  Search string:^(.) Replace string:<@#-unique-#@>\1 Replace-all 

2.对于包含stringhede所有行,删除唯一的“Tag”:

  Search string:<@#-unique-#@>(.*hede) Replace string:\1 Replace-all 

3.在这一点上,所有以独特的“标签”开始的行, 包含stringhede 。 我现在可以做我的其他事情只有这些线路。

4.完成后,我从所有行中删除唯一的“标记”(replacestring为空):

  Search string:<@#-unique-#@> Replace string:<nothing> Replace-all 

通过PCRE动词(*SKIP)(*F)

 ^hede$(*SKIP)(*F)|^.*$ 

这将完全跳过包含确切stringhede并匹配所有其余行的行。

DEMO

部件的执行:

让我们把上面的正则expression式分解成两部分。

  1. 部分在|之前 符号。 部分不应该匹配

     ^hede$(*SKIP)(*F) 
  2. |之后的部分 符号。 部分应该匹配

     ^.*$ 

第1部分

正则expression式引擎将从第一部分开始执行。

 ^hede$(*SKIP)(*F) 

说明:

  • ^断言我们是在开始。
  • hede匹配stringhede
  • $断言我们在行结束。

所以包含stringhede行将被匹配。 一旦正则expression式引擎看到以下(*SKIP)(*F)注意:您可以将(*F)写为(*FAIL) )动词,它会跳过并使匹配失败。 | 在PCRE动词旁边添加所谓的改变或逻辑OR运算符,其中所有行上的每个字符之间存在匹配所有边界的PCRE动词,除了该行包含确切的stringhede 。 在这里看到演示。 也就是说,它试图匹配剩余string中的字符。 现在第二部分的正则expression式将被执行。

第2部分

 ^.*$ 

说明:

  • ^断言我们是在开始。 即,它匹配除了在hede行中的那个之外的所有行开始。 在这里看到演示。
  • .*在多线模式下. 将匹配除换行符或回车符以外的任何字符。 *会重复前一个字符零次或多次。 所以.*会匹配整条线。 在这里看到演示。

    嘿,为什么你添加。*而不是。+?

    因为.*会匹配空白行,但.+不会匹配空白。 我们希望匹配除了hede之外的所有行,在input中也可能有空行。 所以你必须使用.*而不是.+.+会重复前一个字符一次或多次。 请参阅.* 。 在这里匹配一个空行。

  • $结束的锚点在这里没有必要。

自引入ruby-2.4.1以来,我们可以在Ruby的正则expression式中使用新的Absent运算符

从官方文件

 (?~abc) matches: "", "ab", "aab", "cccc", etc. It doesn't match: "abc", "aabc", "ccccabc", etc. 

因此,在你的情况下^(?~hede)$为你做的工作

 2.4.1 :016 > ["hoho", "hihi", "haha", "hede"].select{|s| /^(?~hede)$/.match(s)} => ["hoho", "hihi", "haha"] 

在你的代码中可能更容易维护两个正则expression式,一个做第一个匹配,然后如果它匹配,运行第二个正则expression式来检查你希望阻塞的exception情况,例如^.*(hede).*然后有合适的你的代码中的逻辑。

好吧,我承认这并不是真正解决发布的问题的答案,它也可能使用比单个正则expression式更多的处理。 但是对于来这里寻求快速应急修复的开发者来说,这个解决scheme不应该被忽视。

TXR语言支持正则expression式否定。

 $ txr -c '@(repeat) @{nothede /~hede/} @(do (put-line nothede)) @(end)' Input 

一个更复杂的例子:匹配以a开始并以z结尾的所有行,但不包含hede子string:

 $ txr -c '@(repeat) @{nothede /a.*z&~.*hede.*/} @(do (put-line nothede)) @(end)' - az <- echoed az abcz <- echoed abcz abhederz <- not echoed; contains hede ahedez <- not echoed; contains hede ace <- not echoed; does not end in z ahedz <- echoed ahedz 

正则expression式否定并不是特别有用,但是当你也有交集的时候,事情就会变得有趣,因为你有一套完整的布尔集操作:你可以expression“匹配这个集合的集合,除了匹配的集合。

由于没有人回答这个问题,我会做。

答案是用POSIX grep ,从字面上来说不可能满足这个要求:

 # grep "Regex for doesn't contain hede" Input 

原因在于POSIX grep只需要使用基本正则expression式 ,而这些expression式对于完成这个任务来说不够强大(由于缺less交替,它们不能parsing正常语言)。

但是,GNU grep实现了允许它的扩展。 特别是\| 是GNU实施BREs的交替操作符。 如果您的正则expression式引擎支持replace,负括号expression式,分组和Kleene星,并且能够锚定到string的开头和结尾,那么这就是您所需要的。

用GNU grep ,它会是这样的:

 grep "^\([^h]\|h\(h\|eh\|edh\)*\([^eh]\|e[^dh]\|ed[^eh]\)\)*\(\|h\(h\|eh\|edh\)*\(\|e\|ed\)\)$" Input 

(在Grail中find并进一步手动优化)。

您也可以使用一个实现扩展正则expression式的工具,比如egrep来摆脱反斜杠:

 egrep "^([^h]|h(h|eh|edh)*([^eh]|e[^dh]|ed[^eh]))*(|h(h|eh|edh)*(|e|ed))$" Input 

这里有一个脚本来testing它(注意它testinput.txt在当前目录中生成一个文件testinput.txt ):

 #!/bin/bash REGEX="^\([^h]\|h\(h\|eh\|edh\)*\([^eh]\|e[^dh]\|ed[^eh]\)\)*\(\|h\(h\|eh\|edh\)*\(\|e\|ed\)\)$" # First four lines as in OP's testcase. cat > testinput.txt <<EOF hoho hihi haha hede h he ah head ahead ahed aheda ahede hhede hehede hedhede hehehehehehedehehe hedecidedthat EOF diff -s -u <(grep -v hede testinput.txt) <(grep "$REGEX" testinput.txt) 

在我的系统中打印:

 Files /dev/fd/63 and /dev/fd/62 are identical 

如预期。

最后,正如大家已经注意到的,如果您的正则expression式引擎支持负向预测,那么这个任务会大大简化。 例如,用GNU grep:

 grep -P '^((?!hede).)*$' Input 

以下function将帮助您获得所需的输出

 <?PHP function removePrepositions($text){ $propositions=array('/\bfor\b/i','/\bthe\b/i'); if( count($propositions) > 0 ) { foreach($propositions as $exceptionPhrase) { $text = preg_replace($exceptionPhrase, '', trim($text)); } $retval = trim($text); } return $retval; } ?> 

A simpler solution is to use the not operator !

Your if statement will need to match "contains" and not match "excludes".

 var contains = /abc/; var excludes =/hede/; if(string.match(contains) && !(string.match(excludes))){ //proceed... 

I believe the designers of RegEx anticipated the use of not operators.

How to use PCRE's backtracking control verbs to match a line not containing a word

Here's a method that I haven't seen used before:

 /.*hede(*COMMIT)^|/ 

怎么运行的

First, it tries to find "hede" somewhere in the line. If successful, at this point, (*COMMIT) tells the engine to, not only not backtrack in the event of a failure, but also not to attempt any further matching in that case. Then, we try to match something that cannot possibly match (in this case, ^ ).

If a line does not contain "hede" then the second alternative, an empty subpattern, successfully matches the subject string.

This method is no more efficient than a negative lookahead, but I figured I'd just throw it on here in case someone finds it nifty and finds a use for it for other, more interesting applications.