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


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

输入:

hoho
hihi
haha
hede

码:

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

期望的输出:

hoho
hihi
haha

3567


起源


可能晚了几年,但出了什么问题: ([^h]*(h([^e]|$)|he([^d]|$)|hed([^e]|$)))*?这个想法很简单。保持匹配,直到看到不需要的字符串的开头,然后只匹配字符串未完成的N-1个案例(其中N是字符串的长度)。这些N-1个案例是“h后跟非e”,“他后面是非d”,“hed后跟非e”。如果您成功通过了这些N-1案件,那么您就成功了 没有 匹配不需要的字符串,以便您可以开始寻找 [^h]* 再次 - stevendesu
@stevendesu:尝试这个'非常非常长的词'或甚至更好的半句话。玩得开心。顺便说一下,这几乎是不可读的。不了解性能影响。 - Peter Schuetze
@PeterSchuetze:当然,对于非常长的单词来说它并不漂亮,但它是一个可行且正确的解决方案。虽然我没有对性能进行测试,但我不会想象它太慢了,因为大多数后面的规则都会被忽略,直到你看到h(或者单词的第一个字母,句子等)。您可以使用迭代连接轻松生成长字符串的正则表达式字符串。如果它可以工作并且可以快速生成,那么可读性是否重要?这就是评论的意思。 - stevendesu
@stevendesu:我甚至晚了,但答案几乎完全错了。一方面,它要求主题包含它不应该具有的“h”,因为任务是“匹配行[不包含特定单词”]。让我们假设您打算使内部组可选,并且模式是锚定的: ^([^h]*(h([^e]|$)|he([^d]|$)|hed([^e]|$))?)*$当“hede”的实例之前是“hede”的部分实例(例如“hhede”)时,这会失败。 - jaytea
这个问题已被添加到 Stack Overflow正则表达式FAQ,在“高级正则表达式”下。 - aliteralmind


答案:


正则表达式不支持逆匹配的概念并不完全正确。您可以使用负面外观来模仿此行为:

^((?!hede).)*$

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

如果你还需要匹配换行符,请使用 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" 前方!)。


4859



我不会说这是正则表达式不好的地方。这种解决方案的便利性非常明显,与程序化搜索相比,性能上的提升通常并不重要。 - Archimaredes
严格来说,负面的低头向前使你的正则表达不规律。 - Peter K
@PeterK,当然,但这是SO,而不是MathOverflow或CS-Stackexchange。人们在这里提问一般都在寻找一个实际的答案。大多数库或工具(如 grep具有正则表达式支持的OP提到的所有功能都具有在理论意义上使它们非常规的特征。 - Bart Kiers
@Bart Kiers,没有冒犯你的回答,只是这种滥用术语让我有点恼火。这里真正令人困惑的部分是严格意义上的正则表达式可以非常符合OP的要求,但是编写它们的通用语言不允许它,这导致(数学上丑陋)变通方法,如前瞻。请参见 这个答案 下面和我的评论(理论上对齐)正确的做法。不用说它在大输入上工作得更快。 - Peter K
万一你想知道如何在vim中这样做: ^\(\(hede\)\@!.\)*$ - baldrs


注意解决方案 才不是 从...开始 “合德”

^(?!hede).*$

通常比解决方案更有效 才不是 包含 “合德”

^((?!hede).)*$

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


606



谢谢,我用它来验证字符串dos不包含数字的序列^((?!\ d {5,})。)* - Samih A
^((?!hede).)*$ 我使用jQuery DataTable插件从数据集中排除字符串 - Alex
你好!我不能写作 才不是 结束 用“hede” 正则表达式。你能帮忙吗? - Aleks Ya
@AleksYa:只使用“包含”版本,并将结束锚包含在搜索字符串中:将字符串更改为“不匹配”,从“hede”更改为“hede $” - Nyerguds
@AleksYa:使用负面lookbehind可以完成未结束的版本: (.*)(?<!hede)$。 @Nyerguds的版本也会起作用,但完全忽略了答案提到的表现点。 - thisismydesign


如果 你只是将它用于grep,你可以使用 grep -v hede 得到所有不包含hede的行。

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


165



提示:逐步过滤掉你不想要的东西:grep -v“hede”| grep -v“hihi”| ...等等。 - Olivier Lalonde
或者只使用一个过程 grep -v -e hede -e hihi -e ... - Olaf Dietsche
要不就 grep -v "hede\|hihi" :) - Putnik
如果您要过滤掉许多模式,请将它们放在文件中并使用 grep -vf pattern_file file - codeforester
或者干脆 egrep 要么 grep -Ev "hede|hihi|etc" 避免尴尬的逃避。 - Amit Naidu


回答:

^((?!hede).)*$

说明:

^字符串的开头, ( 分组并捕​​获到\ 1(0次或更多次(匹配尽可能多的数量)),
(?! 展望未来,看看是否有,

hede你的字符串,

) 超前结束, . 除\ n之外的任何字符,
)* 结束\ 1(注意:因为您在此捕获中使用量词,所以只有最后重复捕获的模式将存储在\ 1)
$ 在可选的\ n之前,以及字符串的结尾


122



在使用多个单词的崇高文本2中为我工作的真棒^((?!DSAU_PW8882WEB2|DSAU_PW8884WEB2|DSAU_PW8884WEB).)*$“ - Damodar Bashyal
@DamodarBashyal我知道我已经很晚了,但你可以完全删除那里的第二个词,你会得到完全相同的结果 - forresthopkinsa


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

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

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

这只是一个完全匹配。为子匹配做这件事甚至会更加尴尬。


90



重要的是要注意这只使用基本的POSIX.2正则表达式,因此当PCRE不可用时,terse更便携。 - Steve-o
我同意。许多(如果不是大多数)正则表达式不是常规语言,并且无法被有限自动机识别。 - ThomasMcLeod
@ThomasMcLeod,Hades32:是否可以说'任何可能的常规语言的范围'不'和'和'以及'要么'表达'如'(hede|Hihi)“? (这可能是CS的问题。) - James Haigh
@JohnAllen: 我!!! ......好吧,不是实际的正则表达式,而是学术参考,这也与计算复杂性密切相关; PCRE从根本上不能保证与POSIX正则表达式相同的效率。 - James Haigh
对不起 - 这个答案不起作用,它会匹配hhehe甚至部分匹配hehe(下半场) - Falco


如果你想要正则表达式测试 只要 如果失败了 整个字符串 匹配,以下将工作:

^(?!hede$).*

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

当然,如果你正在检查 精确 平等,在这种情况下更好的通用解决方案是检查字符串相等,即

myStr !== 'foo'

你甚至可以把这个否定  测试是否需要任何正则表达式功能(此处,不区分大小写和范围匹配):

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

然而,在需要正面正则表达式测试的情况下(可能通过API),顶部的正则表达式解决方案可能会有所帮助。


49



跟踪空格怎么样?例如,如果我想测试失败的字符串 " hede "? - eagor
@eagor \s 指令匹配单个空白字符 - Roy Tinker
谢谢,但我没有设法更新正则表达式使这项工作。 - eagor
@eagor: ^(?!\s*hede\s*$).* - Roy Tinker


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


48



有些工具,特别是mysqldumpslow,只提供这种方式来过滤数据,所以在这种情况下,找到一个正则表达式来做这个是除了重写工具之外的最佳解决方案(MySQL AB / Sun没有包含这方面的各种补丁) / Oracle。 - FGM
完全与我的情况相似。 Velocity模板引擎使用正则表达式来决定何时应用转换(转义html),并且我希望它在一种情况下始终工作在EXCEPT之外。 - Henno Vermeulen
还有什么选择?除了正则表达式之外,我从未遇到任何可以进行精确字符串匹配的东西。如果OP使用编程语言,可能还有其他工具可用,但如果他/她不使用编写代码,则可能没有其他选择。 - kingfrito_5005
许多非假设场景中的一个,其中正则表达式是最佳选择:我在IDE(Android Studio)中显示日志输出,并且提供的唯一过滤工具是:普通字符串和正则表达式。尝试使用普通字符串执行此操作将完全失败。 - LarsH