正则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.删除所有包含string
hede
(replacestring为空):
Search string:<@#-unique-#@>.*hede.*\n Replace string:<nothing> Replace-all
3.在这一点上,所有其余的行不包含string
hede
。 从所有行中删除唯一的“标签”(replacestring为空):
Search string:<@#-unique-#@> Replace string:<nothing> Replace-all
现在你有原始文本,包含hede
删除的所有行。
如果我正在寻找做别的只有线不包含stringhede
,我会这样做:
1.search/replace整个文件,在包含任何文本的每一行的开头添加一个唯一的“标签”。
Search string:^(.) Replace string:<@#-unique-#@>\1 Replace-all
2.对于包含string
hede
所有行,删除唯一的“Tag”:
Search string:<@#-unique-#@>(.*hede) Replace string:\1 Replace-all
3.在这一点上,所有以独特的“标签”开始的行, 不包含string
hede
。 我现在可以做我的其他事情只有这些线路。
4.完成后,我从所有行中删除唯一的“标记”(replacestring为空):
Search string:<@#-unique-#@> Replace string:<nothing> Replace-all
通过PCRE动词(*SKIP)(*F)
^hede$(*SKIP)(*F)|^.*$
这将完全跳过包含确切stringhede
并匹配所有其余行的行。
DEMO
部件的执行:
让我们把上面的正则expression式分解成两部分。
-
部分在
|
之前 符号。 部分不应该匹配 。^hede$(*SKIP)(*F)
-
|
之后的部分 符号。 部分应该匹配 。^.*$
第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.