我希望在正则表达式(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实现这一目标?
谢谢, 格特。
答案 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 ...
答案 4 :(得分:0)
我认为这是两个任务,我根本不需要正则表达式。
首先搜索包含所需字符串(“def”)的字符串,然后仅当字符串通过第一次测试时,验证它不包含禁用字符串(“hij”)。
根据哪个更有可能消除最多的可能性,将其作为第一步。例如,如果更多的字符串更可能包含禁用字符串,请首先进行检查,因为您的代码效率会更高。