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

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

输入:

hoho
hihi
haha
hede

码:

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

所需的输出:

hoho
hihi
haha

答案

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

^((?!hede).)*$

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

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

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

或内联使用:

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

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

如果 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"将失败,因为在e3 ,正则表达式(?!hede)失败( "hede"了未来!)。

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

^(?!hede).*$

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

^((?!hede).)*$

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

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

ETA 哦,重新阅读一下问题, grep -v可能就是您所说的 “工具选项”。

回答:

^((?!hede).)*$

说明:

^字符串的开头(并捕获到 \ 1(0 次或多次(匹配最大数量))),
(?!向前看是否没有,

hede你的弦,

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

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

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

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

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

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

^(?!hede$).*

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

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

myStr !== 'foo'

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

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

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

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

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

首先定义表达式的类型:例如,标签是从az字母( lal_char )(当然,在使用补码时定义字母非常重要),每个单词的 “值” 为只是一个布尔值: 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 网上出现

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

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

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

这是改进的正则表达式:

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

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

这是演示代码

有关前瞻的更多信息,请查看出色的文章: Mastering Lookahead 和 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()
);

基准测试

我决定评估一些介绍的选件并比较它们的性能,并使用一些新功能。 .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 解决方案应该更快,更易读。