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

r8uurelv  于 2021-09-29  发布在  Java
关注(0)|答案(17)|浏览(410)

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

输入:

hoho
hihi
haha
hede

代码:

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

期望输出:

hoho
hihi
haha
wbgh16ku

wbgh16ku1#

使用负前瞻,正则表达式可以匹配不包含特定模式的内容。巴特·基尔斯对此进行了回答和解释。很好的解释!
然而,根据巴特·基尔斯的回答,前瞻部分将在匹配任何单个字符时测试1到4个字符。我们可以避免这种情况,让先行部分检查整个文本,确保没有“hede”,然后正常部分(*)可以一次吃掉整个文本。
以下是改进的正则表达式:

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

请注意,负前瞻部分中的(?)惰性量词是可选的,您可以使用()贪婪量词,具体取决于您的数据:如果“hede”确实存在,并且在文本的前半部分,惰性量词可以更快;否则,贪婪的量词会更快。但是,如果“hede”不存在,则两者的速度将相等。
这是演示代码。
有关前瞻的更多信息,请查看这篇伟大的文章:掌握前瞻和落后。
另外,请查看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()
);
rjee0c15

rjee0c152#

既然没有人对所问的问题给出直接的答案,我就这么做。
答案是posix grep ,不可能真正满足这个要求:

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

原因是posix grep 只需要使用基本正则表达式,而基本正则表达式的功能不足以完成该任务(由于缺少替换,它们无法解析所有正则语言)

plicqrtu

plicqrtu3#

如果要匹配字符以否定类似于否定字符类的单词:
例如,字符串:

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

68de4m5k4#

在我看来,这是一个更具可读性的顶级答案变体:

^(?!.*hede)

基本上,“当且仅当行的开头没有“hede”时才匹配”-因此需求几乎直接转化为正则表达式。
当然,可能有多个故障需求:

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

详细信息:^锚点确保正则表达式引擎不会在字符串中的每个位置重试匹配,这将匹配每个字符串。
开头的^锚点表示行的开头。grep工具一次匹配一行,在处理多行字符串的上下文中,可以使用“m”标志:

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

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

hts6caw35#

我是这样做的:

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

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

ezykj2lf

ezykj2lf6#

前述 (?:(?!hede).)* 是伟大的,因为它可以被锚定。

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

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

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

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

这种简化可以添加“和”条款:

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

3xiyfsfu7#

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

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

相当于(对于.net):

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

旧答案:

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

mxg2im7a8#

不是正则表达式,但我发现将串行greps与管道一起使用以消除噪声是合乎逻辑且有用的。
搜索一个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的逻辑是(不是注解)和(匹配目录)

b5buobof

b5buobof9#

基准

我决定评估一些提供的选项,比较它们的性能,并使用一些新功能。在.net正则表达式引擎上进行基准测试:http://regexhero.net/tester/

基准文本:

前7行不应该匹配,因为它们包含搜索的表达式,而下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解决方案应该更快,更容易阅读。

kmb7vmvb

kmb7vmvb10#

regex不支持反向匹配的概念并不完全正确。您可以通过使用负面环视来模拟此行为:

^((?!hede).)*$

上面的正则表达式将匹配不包含(子)字符串“hede”的任何字符串或不带换行符的行。如前所述,这不是正则表达式“擅长”的(或应该做的),但仍然是可能的。
如果还需要匹配换行符,请使用“点全部”修改器(尾部 s 按照以下模式):

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

或者内联使用:

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

(凡 /.../ 是正则表达式分隔符,即不是模式的一部分)
如果“点全部”修改器不可用,则可以模拟角色类的相同行为 [\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" 被看到,如果是这样的话(看到了其他的东西),那么 . (点)将匹配除换行符以外的任何字符。环视也被称为零宽度Assert,因为它们不使用任何字符。他们只是Assert/验证某些东西。
因此,在我的示例中,首先验证每个空字符串,以查看是否没有空字符串 "hede" 向前,在角色被 . (点)。正则表达式 (?!hede). 将只执行一次,因此将其 Package 在一个组中,并重复零次或多次: ((?!hede).)* . 最后,锚定输入的开始和结束,以确保使用整个输入: ^((?!hede).)*$ 如您所见,输入 "ABhedeCD" 将失败,因为 e3 ,正则表达式 (?!hede) 失败(有 "hede" 在前面!)。

8wigbo56

8wigbo5611#

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

pcww981p

pcww981p12#

fwiw,因为正则语言(也称为rational语言)在互补下是封闭的,所以总是可以找到一个否定另一个表达式的正则表达式(称为rational表达式)。但实现这一点的工具并不多。
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。

g6ll5ycj

g6ll5ycj13#

如果希望正则表达式测试仅在整个字符串匹配时失败,则以下操作将起作用:

^(?!hede$).*

e、 g.--如果要允许除“foo”之外的所有值(即“foofoo”、“barfoo”和“foobar”将通过,但“foo”将失败),请使用: ^(?!foo$).* 当然,如果要检查精确相等性,在这种情况下更好的一般解决方案是检查字符串相等性,即。

myStr !== 'foo'

如果需要任何正则表达式特性(这里是不区分大小写和范围匹配),甚至可以将否定放在测试之外:

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

但是,在需要进行正则表达式阳性测试(可能是api测试)的情况下,此答案顶部的正则表达式解决方案可能会有所帮助。

b09cbbtk

b09cbbtk14#

给出的答案非常好,只是一个学术观点:
理论计算机科学意义上的正则表达式不能这样做。对他们来说,它必须看起来像这样:

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

这只是一个完整的匹配。在替补比赛中这样做会更加尴尬。

fquxozlt

fquxozlt15#

答复:

^((?!hede).)*$

说明: ^ 字符串的开头, ( 分组并捕获到\1(0次或更多次(匹配尽可能多的数量)), (?! 向前看,看是否有, hede 你的绳子, ) 展望未来, . 任何字符,除了\n, )* 结束\1(注意:由于您在此捕获上使用了量词,因此只有捕获模式的最后一次重复将存储在\1中) $ 在可选的前\n和字符串的结尾

相关问题