正则表达式匹配不包含单词的行


4292

我知道可以匹配一个单词,然后使用其他工具(例如grep -v)将匹配项反转。但是,是否可以匹配不包含特定单词的行,例如hede使用正则表达式?

输入:

hoho
hihi
haha
hede

码:

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

所需的输出:

hoho
hihi
haha

84
大概晚了几年,但是怎么了([^h]*(h([^e]|$)|he([^d]|$)|hed([^e]|$)))*?这个想法很简单。保持匹配,直到看到不需要的字符串的开头,然后仅在N-1个未完成的字符串(其中N是字符串的长度)的情况下匹配。这N-1个情况是“ h跟非e”,“ he跟非d”和“ hed跟非e”。如果您成功通过了这N-1个案例,则说明您成功地将不需要的字符串与匹配,因此您可以[^h]*再次查找
stevendesu 2011年

323
@stevendesu:尝试使用“非常非常长的单词”,甚至更好的半个句子。祝您打字愉快。顺便说一句,它几乎不可读。不了解性能影响。
Peter Schuetze 2012年

13
@PeterSchuetze:可以肯定的说,这不是很漂亮,但这是一个可行且正确的解决方案。尽管我尚未对性能进行测试,但我不会想象它太慢,因为直到看到h(或单词,句子等的第一个字母)后,大多数规则都会被忽略。您可以使用迭代级联轻松地为长字符串生成正则表达式字符串。如果它可行并且可以快速生成,那么可读性重要吗?这就是评论的目的。
stevendesu'2

57
@stevendesu:我什至晚了,但是那个答案几乎是完全错误的。一方面,考虑到任务是“不包含特定单词的匹配行”,它要求主题必须包含“ h”,而不必。让我们假设您要使内部组可选,并且模式已锚定: ^([^h]*(h([^e]|$)|he([^d]|$)|hed([^e]|$))?)*$ 当“ hede”的实例前面带有“ hede”的部分实例(例如在“ hhede”中)时,此操作将失败。
jaytea 2012年

8
此问题已添加到“高级Regex-Fu”下的“ 堆栈溢出正则表达式常见问题解答 ”中。
aliteralmind 2014年

Answers:


5890

正则表达式不支持逆匹配的说法并不完全正确。您可以使用否定环顾模仿此行为:

^((?!hede).)*$

上面的正则表达式将匹配任何包含换行符'hede'的字符串或没有换行符的行。如前所述,这不是正则表达式“擅长”(或应该做)的事情,但它仍然可能。

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

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

或内联使用:

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

(其中/.../是regex分隔符,即不属于模式的一部分)

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

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

说明

字符串只是n字符列表。每个字符之前和之后都有一个空字符串。因此,n字符列表将包含n+1空字符串。考虑字符串"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的是空字符串。正则表达式会(?!hede).向前看,看是否没有子字符串"hede"可以看到,如果是这种情况(那么可以看到其他东西),那么.(点)将匹配除换行符之外的任何字符。环顾四周也称为零宽度断言,因为它们不占用任何字符。他们仅声明/验证某些内容。

因此,在我的示例中,"hede".(点)占用字符之前,首先对每个空字符串进行验证,以查看是否没有前面的字符串。正则表达式(?!hede).只会执行一次,因此将它包装在一个组中,并重复零次或更多次:((?!hede).)*。最后,锚定输入的开始和结束以确保整个输入都被使用:^((?!hede).)*$

如您所见,输入"ABhedeCD"将失败,因为on上e3的regex (?!hede)失败了( "hede"前方!)。


26
我不会说正则表达式不好。该解决方案的便利性非常明显,与程序搜索相比,性能下降通常并不重要。
Archimaredes

29
严格说来,负前瞻性使您的正则表达式不规则。
彼得·K

55
@PeterK,当然可以,但这不是MathOverflow或CS-Stackexchange。人们在这里提出问题通常是在寻求实用的答案。大多数grep具有正则表达式支持的库或工具(例如OP提到的)都具有从理论上讲使其不规则的功能。
Bart Kiers

19
@Bart Kiers,您的回答没有冒犯之处,只是这种术语的滥用使我有些恼火。这里真正令人困惑的部分是,严格意义上的正则表达式可以完全满足OP的要求,但是编写它们的通用语言却不允许这样做,这导致了(数学上很丑陋的)变通方法,例如超前。请参阅下面的答案以及我的评论(从理论上来说)正确的做法。不用说,它在大输入下工作更快。
彼得·K

17
如果您想知道如何在vim中执行此操作:^\(\(hede\)\@!.\)*$
baldrs

738

请注意,解决方案并非 “ hede” 开头

^(?!hede).*$

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

^((?!hede).)*$

前者仅在输入字符串的第一个位置而不是每个位置检查“ hede”。


5
谢谢,我用它来验证字符串不包含数字的序列^((?!\ d {5,})。)*
Samih A

2
你好!我不能撰写并没有结束与“合德”正则表达式。你能帮忙吗?
亚历克斯·雅

1
@AleksYa:只需使用“包含”版本,并将末尾锚点包含在搜索字符串中:将字符串从“
hede

2
@AleksYa:可以使用否定的lookbehind如:来完成不结束版本(.*)(?<!hede)$。@Nyerguds的版本也可以使用,但是完全错过了答案中提到的性能方面。
thisismydesign

5
为什么这么多答案在说^((?!hede).)*$?使用起来不是更有效^(?!.*hede).*$吗?它做同样的事情,但是步骤更少
JackPRead '19

208

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

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


22
提示:逐步过滤掉不需要的内容:grep -v“ hede” | grep -v“ hihi” | ...等等。
Olivier Lalonde

51
或仅使用一个过程grep -v -e hede -e hihi -e ...
Olaf Dietsche 2015年

15
或者只是grep -v "hede\|hihi":)
Putnik

2
如果您要过滤掉许多模式,请将它们放在文件中并使用grep -vf pattern_file file
codeforester

4
或者干脆egrepgrep -Ev "hede|hihi|etc"避免尴尬转义。
阿米特·奈杜

159

回答:

^((?!hede).)*$

说明:

^字符串的开头, (分组并捕​​获到\ 1(0次或多次(匹配尽可能多的次数)),
(?!向前看是否没有,

hede 你的绳子

)前瞻结尾, .\ n以外的任何字符
)*,\ 1的结尾(注意:由于在此捕获中使用了量词,因此,在捕获的模式中仅将LAST重复的捕获模式存储在\ 1中),
$在可选\ n之前,和字符串的结尾


14
很棒,它在我的崇高文字2中使用多个单词' ^((?!DSAU_PW8882WEB2|DSAU_PW8884WEB2|DSAU_PW8884WEB).)*$' 为我工作
Damodar Bashyal 2015年

3
@DamodarBashyal我知道我来晚了,但是您可以完全删除那里的第二个学期,并且您会得到完全相同的结果
forresthopkinsa

99

给出的答案非常好,只是一个学术观点:

在理论计算机科学的意义正则表达式不能像这样做。对于他们来说,它看起来必须像这样:

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

这只会进行完全匹配。进行子比赛甚至更加尴尬。


1
需要注意的重要一点是,这仅使用基本的POSIX.2正则表达式,因此,在PCRE不可用时,terse更易于移植。
史蒂夫·

5
我同意。即使不是大多数,大多数正则表达式也不是正则语言,因此有限自动机无法识别。
ThomasMcLeod 2014年

@ThomasMcLeod,Hades32:能够在任何可能的常规语言领域中说“ ”,“ ”以及诸如“ ”之类的表达式的“ (hede|Hihi) ”吗?(这可能是CS的问题。)
James Haigh 2014年

7
@JohnAllen:我!…嗯,不是实际的正则表达式而是学术参考,它也与计算复杂性密切相关;从根本上说,PCRE无法保证与POSIX正则表达式相同的效率。
James Haigh 2014年

4
抱歉-该答案不起作用,将匹配该词,甚至部分匹配(下半场)
Falco

60

如果您只想让正则表达式测试整个字符串匹配时才失败,则可以执行以下操作:

^(?!hede$).*

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

当然,如果要检查完全相等,则在这种情况下更好的常规解决方案是检查字符串是否相等,即

myStr !== 'foo'

如果需要任何正则表达式功能(此处不区分大小写和范围匹配),甚至可以将否定项放在测试之外

!/^[a-f]oo$/i.test(myStr)

但是,在需要积极的正则表达式测试的情况下(也许通过API),此答案顶部的正则表达式解决方案可能会有所帮助。


尾随空格怎么办?例如,如果我想测试失败并出现string " hede "
渴望1998年

@eagor \s指令匹配单个空格字符
Roy Tinker

谢谢,但是我没有设法更新正则表达式来完成这项工作。
渴望1998年

2
@eagor:^(?!\s*hede\s*$).*
罗伊·廷克

52

FWIW,由于正则语言(又称有理语言)是在补语的基础上关闭的,因此总是有可能找到否定另一个表达式的正则表达式(即有理表达式)。但是没有多少工具可以实现这一点。

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

标签信(:你先定义表达式的类型lal_char)来接从az(与互补工作时定义的字母,当然是非常重要的),例如,与“价值”计算每个字仅仅是一个布尔:true这个词被接受,false,被拒绝。

在Python中:

In [5]: import vcsn
        c = vcsn.context('lal_char(a-z), 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}  𝔹

然后输入表达式:

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

将此表达式转换为自动机:

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

对应的自动机

最后,将此自动机转换回简单表达式。

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网上出现


6
正确,但是很丑陋,仅适用于小型字符集。您不想使用Unicode字符串:-)
reinierpost

有更多工具允许使用,其中最令人印象深刻的就是Ragel。此处将其表示为(any *-('hehe'any *))表示开始对齐的匹配,或表示为(any *-('hehe'any *))表示未对齐的匹配。
彼得·K

1
@reinierpost:为什么丑陋,Unicode有什么问题?我不能在两者上都同意。(我没有vcsn的经验,但有DFA的经验)。
彼得·K

3
@PedroGimeno锚定时,您确定先将此正则表达式放在括号中吗?否则,锚点和之间的优先顺序|将无法正常播放。 '^(()|h(ed?)?|([^h]|h([^e]|e([^d]|d([^e]|e.)))).*)$'
akim

1
我认为值得一提的是,此方法用于匹配不是单词“ hede”的行,而不是匹配OP不要求的不包含单词“ hede”的行。见我对后者的回答。
Pedro Gimeno

51

很好地解释了为什么对任意正则表达式求反并不容易。不过,我必须同意其他答案:如果这不是假设的问题,那么正则表达式不是正确的选择。


10
某些工具,特别是mysqldumpslow,仅提供这种方式来过滤数据,因此,在这种情况下,除了重写工具外,找到一个正则表达式是最好的解决方案(MySQL AB / Sun并未提供针对此的各种补丁程序) /甲骨文。
FGM

1
完全与我的情况相似。Velocity模板引擎使用正则表达式来决定何时应用转换(转义html),我希望它在一种情况下始终可以正常工作。
Henno Vermeulen

1
有什么选择吗?除正则表达式外,我从未遇到过任何可以进行精确字符串匹配的事情。如果OP使用一种编程语言,则可能还有其他工具可用,但是如果他/她使用的不是编写代码,则可能没有其他选择。
kingfrito_5005

2
正则表达式是最佳选择的许多非假设场景之一:我在显示日志输出的IDE(Android Studio)中,唯一提供的过滤工具是:纯字符串和正则表达式。尝试使用纯字符串执行此操作将完全失败。
LarsH 2013年

48

通过负前瞻,正则表达式可以匹配不包含特定模式的内容。这是Bart Kiers的回答和解释。很好的解释!

但是,根据Bart Kiers的回答,超前部分将在匹配任何单个字符的同时测试1-4个字符。我们可以避免这种情况,让前瞻部分检查整个文本,确保没有“ hede”,然后正常部分(。*)可以一次吃掉整个文本。

这是改进的正则表达式:

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

请注意,负前瞻部分中的(*?)惰性量词是可选的,您可以根据数据使用(*)贪婪量词:如果确实存在'hede'并且在文本的开头,则该惰性量词可以更快 否则,贪婪的量词会更快。但是,如果不出现“ hede”,则两者的速度将相等。

这是演示代码

有关前瞻的更多信息,请查看出色的文章:Mastering Lookahead and Lookbehind

另外,请查看RegexGen.js,这是一个JavaScript正则表达式生成器,可帮助构造复杂的正则表达式。使用RegexGen.js,您可以以更易读的方式构造正则表达式:

var _ = regexGen;

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

3
因此只需检查给定的字符串是否不包含str1和str2:^(?!.*(str1|str2)).*$
S.Serpooshan

1
是的,或者您可以^(?!.*?(?:str1|str2)).*$根据数据使用惰性量词:。添加了,?:因为我们不需要捕获它。
amobiz'3

到目前为止,这是最佳答案,约为10xms。如果将jsfiddle代码和结果添加到答案中,人们可能会注意到它。我不知道为什么在没有hede的情况下,懒惰版本比贪婪版本要快。他们不应该花相同的时间吗?
user5389726598465

是的,因为它们都测试了整个文本,所以它们花费的时间相同。
amobiz '17

41

基准测试

我决定评估一些介绍的选件并比较它们的性能,并使用一些新功能。.NET Regex Engine上的基准测试:http : //regexhero.net/tester/

基准文本:

前7行不匹配,因为它们包含搜索到的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等),因此我无法测试解决方案P1和P2。

摘要:

我尝试测试大多数建议的解决方案,某些单词可能有一些优化。例如,如果搜索字符串的前两个字母不同,则答案03可以扩展为 ^(?>[^R]+|R+(?!egex Hero))*$较小的性能增益。

但是,使用条件语句,整体上最具可读性和性能方面最快的解决方案似乎是05或带有条件量词的04。我认为Perl解决方案应该更快,更易读。


5
你如果时间^(?!.*hede)太。///另外,最好对匹配的语料库和不匹配的语料库的表达式进行排序,因为通常是大多数行匹配或大多数行不匹配的情况。
ikegami,2016年

32

不是正则表达式,但是我发现将带有管道的串行抓钩用于消除噪声是合乎逻辑且有用的。

例如。搜索没有所有注释的apache配置文件-

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的逻辑是(非注释)和(与dir匹配)


2
我认为他是要求正则表达式版本的grep -v
Angel.King.47

9
这很危险。还错过了good_stuff #comment_stuff
Xavi Montero

29

这样,您就可以避免在每个位置上进行前瞻性测试:

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

相当于(.net):

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

旧答案:

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

7
好点子; 我很惊讶以前没有人提到这种方法。但是,当将特定的正则表达式应用于不匹配的文本时,容易发生灾难性的回溯。这是我的处理方式:/^[^h]*(?:h+(?!ede)[^h]*)*$/
艾伦·摩尔

...或者您可以使所有的量词都具有所有格。;)
艾伦·摩尔

@艾伦·摩尔-我也很惊讶。只有在下面的答案中发布了相同的模式之后,我才在这里看到您的评论(以及堆中最好的正则表达式)。
ridgerunner 2013年

@ridgerunner,不一定是最好的。我看过基准测试,其中最佳答案的表现更好。(我对此感到惊讶。)
Qtax

23

前述的伟大之处(?:(?!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

20

这是我的处理方式:

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

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


17

如果要匹配字符以否定与否定字符类类似的单词:

例如,一个字符串:

<?
$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"

3
在perl regexp中没有“ lookcurrent”。这确实是一个负面的前瞻性(前缀(?!)。正向超前前缀为,(?=而相应的向后超前前缀分别为(?<!(?<=。前瞻意味着您不消耗下一个字符(因此称为“ ahead”)。后面的意思是您检查已经消耗的字符。
Didier L

14

在我看来,最佳答案的可读性更高:

^(?!.*hede)

基本上,“仅当行中没有'hede'时,才在行的开头进行匹配”-因此,要求几乎直接转换为regex。

当然,可能有多种故障要求:

^(?!.*(hede|hodo|hada))

细节: ^锚确保正则表达式引擎不会在字符串的每个位置(将匹配每个字符串)重试匹配项。

开头的^锚点代表行的开头。grep工具一次匹配每一行,在使用多行字符串的情况下,可以使用“ m”标志:

/^(?!.*hede)/m # JavaScript syntax

要么

(?m)^(?!.*hede) # Inline flag

多次否定的出色示例。
彼得·帕拉达

从上面的答案一个区别是,这不符合任何东西,而且整条生产线相匹配,如果不“合德”
Z. Khullah

13

OP未指定或Tag将使用正则表达式说明要使用的上下文(编程语言,编辑器,工具)。

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

Textpad 支持某些正则表达式,但不支持向前或向后查找,因此需要一些步骤。

如果我希望保留所有包含该字符串的行hede,则可以这样做:

1.搜索/替换整个文件,以在包含任何文本的每一行的开头添加一个唯一的“标记”。

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

2.删除所有包含字符串的行hede(替换字符串为空):

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

3.此时,所有剩余的行都不包含字符串hede。从所有行中删除唯一的“ Tag”(替换字符串为空):

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

现在,您拥有了原始文本,并且hede删除了包含该字符串的所有行。


如果我只想对包含字符串的行执行其他操作,则可以这样做:hede

1.搜索/替换整个文件,以在包含任何文本的每一行的开头添加一个唯一的“标记”。

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

2.对于所有包含字符串的行hede,删除唯一的“ Tag”:

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

3.此时,所有以唯一的“标记”开头的行都不要包含字符串hede。我现在可以做我的别的东西,只有那些行。

4.完成后,从所有行中删除唯一的“ Tag”(替换字符串为空):

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

12

由于没有其他人给予直接回答的问题是有人问,我会做到这一点。

答案是,使用POSIX grep不可能从字面上满足此请求:

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

原因是grep仅需要POSIX 才能使用基本正则表达式,而后者根本不足以完成该任务(由于缺少交替和括号,它们无法解析正则语言)。

但是,GNU grep实现了允许它的扩展。特别\|是GNU的BRE实现中的交替运算符,\(并且\)是括号。如果您的正则表达式引擎支持交替,负括号表达式,括号和Kleene星号,并且能够锚定到字符串的开头和结尾,那么这就是您所需要的。但是请注意,除负数集[^ ... ]外,它们还非常方便,因为否则,您需要用以下形式的表达式来替换它们:该表达式(a|b|c| ... )列出了不在集合中的每个字符,这非常繁琐且过长,甚至在整个字符集是Unicode。

使用GNU grep,答案将是这样的:

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

(在Grail中找到,并进行了一些手动优化)。

您还可以使用实现扩展正则表达式的工具(例如egrep)来消除反斜杠:

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

这是一个测试它的脚本(注意它会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

如预期的那样。

对于那些对细节感兴趣的人,所采用的技术是将与单词匹配的正则表达式转换为有限的自动机,然后通过将每个接受状态更改为非接受来反转自动机,反之亦然,然后将结果FA转换为正则表达式。

最后,正如每个人都指出的那样,如果您的正则表达式引擎支持否定的提前查询,则可以大大简化任务。例如,使用GNU grep:

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

更新:我最近发现Kendall Hopkins的出色的FormalTheory库,用PHP编写,它提供类似于Grail的功能。使用它,以及由我自己编写的简化程序,我已经能够编写一个带有给定输入短语(当前仅支持字母数字和空格字符)的否定正则表达式的在线生成器:http : //www.formauri.es/personal/ pgimeno / misc / non-match-regex /

对于hede它的输出:

^([^h]|h(h|e(h|dh))*([^eh]|e([^dh]|d[^eh])))*(h(h|e(h|dh))*(ed?)?)?$

与上述相同。


11

自从引入ruby-2.4.1以来,我们可以在Ruby的正则表达式中使用新的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"]

9

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

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

这将完全跳过包含确切字符串hede并与所有其余行匹配的行。

演示

零件执行:

让我们将上面的正则表达式分为两部分来考虑。

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

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

    ^.*$

第1部分

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

^hede$(*SKIP)(*F)

说明:

  • ^ 断言我们处于起步阶段。
  • hede 匹配字符串 hede
  • $ 断言我们处于行尾。

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

第2部分

^.*$

说明:

  • ^ 断言我们处于起步阶段。即,它匹配除行中的一个以外的所有行开头hede。在这里查看演示。
  • .*在多行模式下,.将匹配除换行符或回车符以外的任何字符。并将*前一个字符重复零次或更多次。所以.*会匹配整行。在这里查看演示。

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

    因为.*会匹配一个空白行,但.+不会匹配一个空白行。我们要匹配所有行hede,但输入中可能还会有空白行。因此您必须使用.*而不是.+.+将重复前一个字符一次或多次。请参阅此处.*匹配空白行。

  • $ 此处不需要线锚。


7

您的代码中的两个正则表达式可能更易于维护,一个可以进行第一个匹配,然后如果匹配则运行第二个正则表达式以检查您希望阻止的异常情况,例如,^.*(hede).*然后在代码中包含适当的逻辑。

好的,我承认这并不是对发布的问题的真正答案,它也可能比单个正则表达式使用更多的处理。但是对于那些来这里寻求针对异常情况的快速紧急修复的开发人员来说,此解决方案不容忽视。


5

TXR语言支持正则表达式的否定。

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

一个更复杂的示例:匹配以a和开头z但不包含substring的所有行hede

$ 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

正则表达式取反本身并不是特别有用,但是当您也有交集时,事情会变得很有趣,因为您拥有完整的布尔集操作集:您可以表达“与此匹配的集合,除了与之匹配的集合”。


请注意,这也是基于ElasticSearch Lucene的正则表达式的解决方案。
WiktorStribiżew的

5

另一个选择是添加一个正向的前瞻性并检查hehe输入行中是否存在,然后我们将其取反,其表达式类似于:

^(?!(?=.*\bhede\b)).*$

带有单词边界。


该表达式在的右上方面板中进行了解释 regex101.com,如果您希望对其进行探索/简化/修改,并且在此链接中,您可以根据需要观看它与某些示例输入的匹配方式。


RegEx电路

jex.im可视化正则表达式:

在此处输入图片说明


4

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

<?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;
    }


?>

2

^((?! hede)。)* $是一种优雅的解决方案,但由于它会消耗字符,因此您无法将其与其他条件结合使用。例如,假设您要检查是否不存在“ hede”和是否存在“ haha​​”。此解决方案将起作用,因为它不会消耗字符:

^(?!。\ bhede \ b)(?=。 \ bhaha \ b)


1

如何使用PCRE的回溯控制动词来匹配不包含单词的行

这是我以前从未见过的方法:

/.*hede(*COMMIT)^|/

怎么运行的

首先,它尝试在行中的某个地方找到“ hede”。如果成功,则此时将(*COMMIT)通知引擎,不仅在发生故障时不回退,而且在这种情况下也不要尝试任何进一步的匹配。然后,我们尝试匹配一些可能不匹配的内容(在本例中为^)。

如果一行不包含“ hede”,则第二种选择(空的子模式)成功匹配主题字符串。

这种方法并不比否定先行有效,但是我认为我只是把它放在这里,以防有人发现它很漂亮并在其他更有趣的应用程序中找到用处。


0

一个更简单的解决方案是使用not运算符

您的if语句将需要匹配“包含”而不是“排除”。

var contains = /abc/;
var excludes =/hede/;

if(string.match(contains) && !(string.match(excludes))){  //proceed...

我相信RegEx的设计人员期望使用not运算符。


0

也许您会在尝试编写一个正则表达式时在Google上找到此正则表达式,该正则表达式能够匹配包含子字符串的行段(而不是整行)。花了一段时间让我弄清楚,所以我将分享:

给定一个字符串: <span class="good">bar</span><span class="bad">foo</span><span class="ugly">baz</span>

我想匹配<span>不包含子字符串“坏”的标签。

/<span(?:(?!bad).)*?>将匹配<span class=\"good\"><span class=\"ugly\">

请注意,括号有两套(层):

  • 最里面的一个用于负向超前(不是捕获组)
  • Ruby将最外面的对象解释为捕获组,但我们不希望它成为捕获组,因此我添加了?:在开始时,不再将其解释为捕获组。

Ruby中的演示:

s = '<span class="good">bar</span><span class="bad">foo</span><span class="ugly">baz</span>'
s.scan(/<span(?:(?!bad).)*?>/)
# => ["<span class=\"good\">", "<span class=\"ugly\">"]

0

使用ConyEdit,可以使用命令行cc.gl !/hede/获取不包含正则表达式匹配项的行,也可以使用命令行cc.dl /hede/删除包含正则表达式匹配项的行。他们有相同的结果。


0

如果您要匹配包含字符串X但不包含字符串Y的整行,我想添加另一个示例

例如,假设我们要检查我们的URL /字符串是否包含“ taste-treats ”,只要它在任何地方都不包含“ Chocolate ”即可。

这种正则表达式模式可以工作(在JavaScript中也可以工作)

^(?=.*?tasty-treats)((?!chocolate).)*$

(例如,全局,多行标志)

互动示例:https//regexr.com/53gv4

火柴

(这些url包含“好吃的东西”,也不包含“ chocolate”)

  • example.com/tasty-treats/strawberry-ice-cream
  • example.com/desserts/tasty-treats/banana-pudding
  • example.com/tasty-treats-overview

不匹配

(这些网址在某处包含“巧克力”,因此即使它们包含“美味处理”也不会匹配)

  • example.com/tasty-treats/chocolate-cake
  • example.com/home-cooking/oven-roasted-Chicken
  • example.com/tasty-treats/banana-chocolate-fudge
  • example.com/desserts/chocolate/tasty-treats
  • example.com/chocolate/tasty-treats/desserts
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.