正则表达式找到正面和负面的匹配

时间:2014-03-01 22:30:16

标签: regex tcl

我希望在正则表达式(TCL)中匹配正负字符的组合。

假设我想匹配包含'def'且不包含'hij'的行。

ab def hhh    -> print
abdefxxhijzz  -> no print
hij           -> no print
123defhijxyz  -> no print
0def123hijxyz -> no print

我试过了:

{(def)(?!hij)}
{(def).*(?!hij)}
{.*(def).*(?!hij)}
{.*(def).*(?!hij).*}

所有错误地打印'0def123hijxyz'。

在cmd行上,我可以使用2 x grep cmds执行此操作。

echo 0def123hijxyz | grep def | grep -v hij

你们中的一位专家能否帮助regexp实现这一目标?

谢谢, 格特。

5 个答案:

答案 0 :(得分:2)

这个正则表达式应该有效:

(?!.*hij)(.*def.*)

它预测子字符串.*hij,如果找不到,则匹配(.*def.*)

答案 1 :(得分:2)

你很接近,但是你需要先执行负向前瞻,然后将其锚定以确保它只在字符串的开头应用一次。

{(?n)^(?!.*hij).*def.*}
  • (?n)启用-line模式,允许^在一行的开头匹配(大多数正则表达式称为multiline模式)。

  • (?!.*hij)在整个字符串中搜索hij,如果找到则会报告失败。

  • .*def.*如果包含def,则会使用整个字符串。

锚点是必要的,以防止它匹配一个字符串,其中不需要的单词位于所需单词之前,如hij def。没有锚,它可以从i开始找到匹配。

答案 2 :(得分:2)

对于这种检查,我宁愿不使用regexp而是使用字符串方法:

if {[string match *def* "0def123hijxyz"] && ![string match *hij* "0def123hijxyz"]} {
    puts "Match!"
}

string match使用glob匹配,因此*是通配符。

如果[string match *def* "0def123hijxyz"]在字符串中,则

def返回1,否则返回0。


如果您仍坚持使用regexp方法,我会建议使用此正则表达式:

^(?!.*hij).*def

^是行锚点的开头,它使正则表达式只检查一次匹配,而不是在匹配失败时重复检查(即在它发现有hij或没有{之后{1}})。

def中添加.*可以检查整个字符串,而不是字符串中的单个位置。

(?!.*hij)然后尝试匹配.*def。您不必在结尾处使用其他def,除非您想要匹配更多内容,例如.*后跟def,即使其中包含其他字符将是g。最后使用此.*def.*g只会为正则表达式提供更多的工作。


一些标杆......

.*

% proc match {} { if {[string match *def* "0def123hijxyz"] && ![string match *hij* "0def12 3hijxyz"]} { } } % proc regmatch {} { if {[regexp -- {^(?!.*hij).*def} 0def123hijxyz]} { } } % puts [time match 100000] 0.49533 microseconds per iteration % puts [time regmatch 100000] 1.38854 microseconds per iteration % proc regmatcher {} { if {[regexp -- {(?n)^(?!.*hij).*def.*} 0def123hijxyz]} { } } % puts [time regmatcher 100000] 2.23913 microseconds per iteration 比简单字符串方法长2-4倍。

答案 3 :(得分:1)

在测试此类事物时,有助于制作一个小测试程序:

proc check {re} {
    foreach s {"ab def hhh" "abdefxxhijzz" "hij" "123defhijxyz" "0def123hijxyz"} {
        puts "$s => [regexp $re $s]"
    }
}

我们来看看......

% check {(def)(?!hij)}
ab def hhh => 1
abdefxxhijzz => 1
hij => 0
123defhijxyz => 0
0def123hijxyz => 1
% check {.*(def).*(?!hij).*}
ab def hhh => 1
abdefxxhijzz => 1
hij => 0
123defhijxyz => 1
0def123hijxyz => 1

大!我们现在可以尝试任何我们可能会想到的针对我们所有测试用例的RE。在编写自己的RE时,这是一种非常有用的技术,并且您已经有了一组测试。


那么......我们需要的RE可能是什么?好吧,我们需要一个肯定的def和一个否定的hij,并且负hij需要在字符串中的每个位置应用。你必须这样想,因为Tcl的负前瞻约束总是使用非贪婪的规则来匹配。

让我们切入追逐。您正在寻找的RE是^(?!.*hij.*$).*def

% check {^(?!.*hij.*$).*def}
ab def hhh => 1
abdefxxhijzz => 0
hij => 0
123defhijxyz => 0
0def123hijxyz => 0

这是有效的,因为我们首先要求从字符串的开头开始匹配(默认情况下Tcl的RE是未锚定的)。然后我们进行了一个否定的预测,即我们不能在“here”(开头)和字符串结尾之间的某个地方匹配hij;没有锚定,这也可能通过不匹配其他地方而成功(自动机理论匹配器就像那样诡计多端)。最后一部分是一个简单的积极的“发现def”。

要了解为什么锚定很重要,请看这个非常类似的。

% check {(?!^.*hij.*$).*def}
ab def hhh => 1
abdefxxhijzz => 1
hij => 0
123defhijxyz => 1
0def123hijxyz => 1

为什么会失败?好吧,考虑在第一个字母后尝试开始匹配;否定的前瞻总是成功,因为那个锚失败了。

您还需要小心测试用例:

% check {def(?!.*hij)}
ab def hhh => 1
abdefxxhijzz => 0
hij => 0
123defhijxyz => 0
0def123hijxyz => 0

看起来很好很短,但是abhijcdefxx失败了; hij位于def之前,因此不会导致问题。


一般来说,如果您正在处理过滤一系列行,我建议实际使用:

# Read lines into list in $lines variable

# Positive filter
set linesWithDef [lsearch -all -inline -regexp $lines {def}]

# Negative filter
set linesWithoutHij [lsearch -all -inline -not -regexp $linesWithDef {hij}]

这在精神上与使用管道grep s ...

的shell构造更相似

答案 4 :(得分:0)

我认为这是两个任务,我根本不需要正则表达式。

首先搜索包含所需字符串(“def”)的字符串,然后仅当字符串通过第一次测试时,验证它不包含禁用字符串(“hij”)。

根据哪个更有可能消除最多的可能性,将其作为第一步。例如,如果更多的字符串更可能包含禁用字符串,请首先进行检查,因为您的代码效率会更高。