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

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

输入:
hohohihihahahede
代码:
grep "<Regex for 'doesn't contain hede'>" input
期望输出:
hohohihihaha
4595387 次浏览

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

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

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

正则表达式不支持反向匹配的概念并不完全正确。您可以通过使用否定查找来模仿这种行为:

^((?!hede).)*$

非捕获变体:

^(?:(?!: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上,regex(?!hede)失败(前面有"hede"!)。

不是regex,但我发现使用带有管道的串行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的逻辑是(不是注释)和(匹配dir)

注意不以"hede"开头的解:

^(?!hede).*$

通常比不包含"hede"的解决方案更有效:

^((?!hede).)*$

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

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

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

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

这只做了一个完整的匹配。做子匹配甚至会更尴尬。

如果你想匹配一个字符来否定一个类似于否定字符类的单词:

例如,一个字符串:

<?$str="aaa        bbb4      aaa     bbb7";?>

不要使用:

<?preg_match('/aaa[^bbb]+?bbb7/s', $str, $matches);?>

用途:

<?preg_match('/aaa(?:(?!bbb).)+?bbb7/s', $str, $matches);?>

注意"(?!bbb)."既不是look的后面,也不是look的前面,它是look的当前,例如:

"(?=abc)abcde", "(?!abc)abcde"

如果您希望regex测试在整个字符串匹配时只有失败,以下操作将起作用:

^(?!hede$).*

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

当然,如果您要检查确切相等性,在这种情况下,更好的通用解决方案是检查字符串相等性,即。

myStr !== 'foo'

如果您需要任何正则表达式功能(这里,不区分大小写和范围匹配),您甚至可以将否定外面用于测试:

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

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

有了这个,你可以避免在每个位置上测试前瞻性:

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

等价于(对于. net):

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

老答案:

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

OP没有指定或Tag帖子来指示Regex将在其中使用的上下文(编程语言、编辑器、工具)。

对我来说,我有时需要在使用#0编辑文件时这样做。

#0支持一些正则表达式,但不支持向前看或向后看,因此需要几个步骤。

如果我想保留不要包含字符串#0的所有行,我会这样做:

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

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

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

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

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

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

现在您已经删除了包含字符串#0的所有行的原始文本。


如果我希望做点别的只包含不要包含字符串#0的行,我会这样做:

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

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

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

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

3.此时,所有以唯一的“Tag”开头的行,不要都包含字符串#0。我现在可以只对这些行执行别的什么

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

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

我是这样做的:

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

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

答复:

^((?!hede).)*$

说明:

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

hede你的弦,

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

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

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

一个更复杂的例子:匹配所有以a开始并以z结束但不包含子字符串hede的行:

$ txr -c '@(repeat)@{nothede /a.*z&~.*hede.*/}@(do (put-line nothede))@(end)' -az         <- echoedazabcz       <- echoedabczabhederz   <- not echoed; contains hedeahedez     <- not echoed; contains hedeace        <- not echoed; does not end in zahedz      <- echoedahedz

正则表达式否定本身并不是特别有用,但是当你也有交集时,事情就变得有趣了,因为你有一整套布尔集运算:你可以表达“匹配这个的集合,除了匹配那个的东西”。

使用负向前看,正则表达式可以匹配不包含特定模式的东西。这是由巴特·基尔回答和解释的。很好的解释!

然而,根据Bart Kier的答案,预览部分将在匹配任何单个字符时提前测试1到4个字符。我们可以避免这种情况,让预览部分检查整个文本,确保没有“hede”,然后正常部分(.*)可以一次吃掉整个文本。

以下是改进的regex:

/^(?!.*?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());

基准

我决定评估一些呈现的选项并比较它们的性能,以及使用一些新功能。在. NET Regex引擎上进行基准测试: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 Heroegex 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 Heregex Heroegex 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 Answer02: ^(?:(?!Regex Hero).)*$                  5.034   // With Non-Capturing group03: ^(?!.*?Regex Hero).*                   7.356   // Lookahead at the beginning, if not found match everything04: ^(?>[^R]+|R(?!egex Hero))*$             6.137   // Lookahead only on the right first letter05: ^(?>(?:.*?Regex Hero)?)^.*$             7.426   // Match the word and check if you're still at linestart06: ^(?(?=.*?Regex Hero)(?#fail)|.*)$       7.371   // Logic Branch: Find Regex Hero? match nothing, else anything
P1: ^(?(?=.*?Regex Hero)(*FAIL)|(*ACCEPT))  ?????   // Logic Branch in Perl - Quick FAILP2: .*?Regex Hero(*COMMIT)(*FAIL)|(*ACCEPT) ?????   // Direct COMMIT & FAIL in Perl

由于. NET不支持动作动词(*FAIL等),我无法测试解决方案P1和P2。

总结:

整体上最具可读性和性能方面最快的解决方案似乎是03,带有简单的负向前看。这也是JavaScript的最快解决方案,因为JS不支持其他解决方案更高级的正则表达式功能。

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

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

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

演示

执行部分:

让我们考虑一下上面的正则表达式,把它分成两部分。

  1. |符号前的部分。第1部分。

    ^hede$(*SKIP)(*F)
  2. Part after the | symbol. Part should be matched.

    ^.*$

PART 1

Regex engine will start its execution from the first part.

^hede$(*SKIP)(*F)

说明:

  • ^断言我们处于起点。
  • hede匹配字符串hede
  • $断言我们在行尾。

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

第二部分

^.*$

说明:

  • ^断言我们在起点。即,它匹配除hede行之外的所有行开始。参见演示这里
  • .*在多行模式下,.将匹配除换行符或回车符之外的任何字符。*将重复前一个字符零次或多次。因此.*将匹配整行。请参阅演示这里

    嘿,你为什么加.*而不是.+?

    因为.*会匹配一个空白行,但.+不会匹配一个空白行。我们想匹配除hede之外的所有行,因此输入中可能也有空白行。所以您必须使用.*而不是.+.+会重复前面的字符一次或多次。请参阅.*匹配空白行这里

  • $这里不需要行锚点的结束。

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

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

FWIW,由于正则语言(又名有理语言)在互补下是封闭的,总是可以找到一个否定另一个表达式的正则表达式(又名有理表达式)。但没有多少工具实现这一点。

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

你首先定义表达式的类型:例如,标签是从az中选择的字母(lal_char)(在使用互补时定义字母表当然非常重要),为每个单词计算的“值”只是一个布尔值:true单词被接受,false,拒绝。

在Python中:

In [5]: import vcsnc = vcsn.context('lal_char(a-z), b')cOut[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}'); eOut[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那里

前面提到的(?:(?!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

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

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

var contains = /abc/;var excludes =/hede/;
if(string.match(contains) && !(string.match(excludes))){  //proceed...

我相信RegEx的设计者预料到了not操作符的使用。

既然没有人直接回答问题被要求,我就这么做。

答案是使用POSIXgrep,不可能从字面上满足这个请求:

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

原因是POSIXgrep只需要与基本正则表达式一起工作,它们根本不足以完成该任务(由于缺乏交替,它们无法解析所有常规语言)。

然而,GNUgrep实现了允许它的扩展。特别是,\|是GNU实现BRE中的交替运算符。如果您的正则表达式引擎支持交替、括号和Kleene星号,并且能够锚定到字符串的开头和结尾,这就是这种方法所需要的全部。然而,请注意,除了这些之外,负数集[^ ... ]非常方便,因为否则,您需要将它们替换为形式(a|b|c| ... )的表达式,该表达式列出了不在集合中的每个字符,这非常乏味且过长,如果整个字符集是Unicode,则更是如此。

多亏了形式语言理论,我们可以看到这样的表达式是什么样子的。使用GNUgrep,答案类似于:

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

(发现圣杯和一些手工进行的进一步优化)。

您还可以使用实现扩展正则表达式的工具,例如egrep来摆脱反斜杠:

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

这是一个测试它的脚本(注意它在当前目录中生成一个文件testinput.txt)。提供的几个表达式未通过此测试。

#!/bin/bashREGEX="^\([^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 <<EOFhohohihihahahede
hheahheadaheadahedahedaahedehhedehehedehedhedehehehehehehedehehehedecidedthatEOFdiff -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

但是,这种方法的缺点是它需要回溯正则表达式引擎。这使得它不适合使用安全正则表达式引擎(如RE2)的安装,这是在某些情况下更喜欢生成方法的原因之一。

使用Kendall Hopkins用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?)?)?$

这相当于上面的。

下面的函数将帮助您获得所需的输出

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

?>

自从引入ruby-2.4.1以来,我们可以在Ruby的正则表达式中使用新的缺席运算符

来自官方doc

(?~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"]

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

这里有一个我以前从未见过的方法:

/.*hede(*COMMIT)^|/

它是如何运作的

首先,它试图在行中的某个地方找到“hede”。如果成功,此时,(*COMMIT)告诉引擎,如果失败,不仅不要回溯,而且在这种情况下不要尝试任何进一步的匹配。然后,我们尝试匹配一些不可能匹配的东西(在这种情况下,^)。

如果一行不包含“hede”,则第二个替代方案(空子模式)成功匹配主题字符串。

这种方法并不比消极的前瞻性更有效,但我想我还是把它扔在这里,以防有人发现它很漂亮,并发现它可以用于其他更有趣的应用程序。

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

给定一个字符串:barfoobaz

我想匹配不包含子字符串“bad”的<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\">"]

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

一个,在我看来,顶部答案的更可读的变体:

^(?!.*hede)

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

当然,也可能有多个失败要求:

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

详情:^锚确保正则表达式引擎不会在字符串中的每个位置重试匹配,这将匹配每个字符串。

开头的^锚点旨在表示该行的开头。grep工具一次匹配每一行,在您使用多行字符串的上下文中,您可以使用“m”标志:

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

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

^((?!hede).)*$是一个优雅的解决方案,除了因为它消耗字符,你将无法将其与其他条件组合使用。例如,假设你想检查是否存在“hede”和“haha”。这个解决方案可以工作,因为它不会消耗字符:

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

另一种选择是添加一个积极的前瞻性并检查hede是否在输入行中的任何位置,然后我们将否定它,使用类似于以下的表达式:

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

词的边界。


如果您想探索/简化/修改它,表达式在regex101.com的右上角面板上进行了解释,如果您愿意,您可以在此链接中查看它如何与一些示例输入匹配。


RegEx Circuit

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

在此处输入图片描述

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

例如,假设我们要检查我们的URL/字符串是否包含“美味佳肴”,只要它在任何地方都不包含“巧克力”。

这个regex模式可以工作(在JavaScript中也可以)

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

(例如全局、多行标志)

交互式示例:https://regexr.com/53gv4

匹配

(这些URL包含“美味零食”,也不包含“巧克力”)

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

不匹配

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

  • 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

只要你在处理线,简单地标记负面的匹配然后瞄准剩下的

事实上,我将这个技巧用于ed是因为它看起来不支持^((?!hede).)*$

对于期望的输出

  1. 标记否定匹配:(例如带有hede的行),使用根本不包含在整个文本中的字符。表情符号可能是一个不错的选择。

    s/(.*hede)/🔒\1/g
  2. Target the rest (the unmarked strings: e.g. lines without hede). Suppose you want to keep only the target and delete the rest (as you want):

    s/^🔒.*//g

For a better understanding

Suppose you want to delete the target:

  1. Mark the negative match: (e.g. lines with hede), using a character not included in the whole text at all. An emoji could probably be a good choice for this purpose.

    s/(.*hede)/🔒\1/g
  2. Target the rest (the unmarked strings: e.g. lines without hede). Suppose you want to delete the target:

    s/^[^🔒].*//g
  3. Remove the mark:

    s/🔒//g
# 一个简单的方式import reskip_word = 'hede'stranger_char = '虩'content = '''hohohihihahahede'''print('\n'.join(re.findall('([^{}]*?)\n'.format(stranger_char),content.replace(skip_word, stranger_char))).replace(stranger_char, skip_word))
# hoho# hihi# haha