在TCL PERL中匹配正则表达式

时间:2016-03-16 12:06:29

标签: regex tcl

我有以下模式

    Pattern[1]: 
    Key : "key1" 
    Value : 100
    Pattern[2]: 
    Key : "key2" 
    Value : 20
    Pattern[3]: 
    Key : "key3" 
    Value : 30
    Pattern[4]: 
    Key : "key4" 
    Value : 220

我想隔离每个Pattern块。我正在使用TCL。我正在使用的Regexp无法解决目的

set updateList [regexp -all -inline {Pattern\[\d+\].*?Value.*?\n} $list]

使用哪个Regexp来隔离每个模式

我需要输出

    Pattern[1]: 
    Key : "key1" 
    Value : 100


    Pattern[2]: 
    Key : "key2" 
    Value : 20


    Pattern[3]: 
    Key : "key3" 
    Value : 30


    Pattern[4]: 
    Key : "key4" 
    Value : 220

4 个答案:

答案 0 :(得分:2)

您的模式Pattern\[\d+\].*?Value.*?\n包含混合量词:贪婪和懒惰。 Tcl不像你期望的那样处理混合量词类型,比如PCRE(PHP,Perl),.NET等,它默认为第一个找到的,因为后续的量词继承了前面的量词类型。因此,+之后的\d是贪婪的,因此,所有其他人(在.*?中)也是贪婪的 - 即使你宣称它们是懒惰的。此外,.也匹配Tcl正则表达式中的换行符,因此,您的模式就像this一样。

因此,根据您的正则表达式,您可以使用\d+使\d+?延迟,并使用\n替换(?:\n|$),以匹配换行符和字符串的结尾

set RE {Pattern\[\d+?\].*?Value.*?(?:\n|$)}
set updateList [regexp -all -inline $RE $str]

请参阅IDEONE demo

备选方案1

此外,如果您的输入字符串始终与所有元素具有相同的结构,则可以使用更详细的正则表达式 - PatternKeyValue - 现在:

set updateList [regexp -all -inline {Pattern\[\d+\]:\s*Key[^\n]*\s*Value[^\n]*} $str]

请参阅IDEONE demo,此处为regex demo

由于.可以匹配换行符,因此我们需要使用[^\n]否定字符类来匹配除换行符之外的任何字符。

备选方案2

您可以使用与Pattern[n]:匹配的展开的延迟子模式,然后使用不是Pattern[n]:序列起点的任何字符:

set RE {Pattern\[\d+\]:[^P]*(?:P(?!attern\[\d+\]).)*}
set updateList [regexp -all -inline $RE $str]

请参阅another IDEONE demoregex101 demo

答案 1 :(得分:1)

试试这个

Pattern\[\d+\](.|\n)*?Value.*?\n

字符匹配任何字符但换行符,因此您需要将其添加进去。请注意,您的行可能以滑块字符结尾,因此您可能需要添加 \ r in。

答案 2 :(得分:1)

您想要捕获线条块并输出它们之间的空白行。您的示例数据显示不同级别的模式,可用于识别哪些行属于哪个块。

最简单的模式是:输入中的每三行组成一个块。这种模式建议像这样处理:

set lines [split [string trim $list \n] \n]
foreach {a b c} $lines {puts $a\n$b\n$c\n\n}

您的示例数据中没有任何内容表明这不起作用。尽管如此,可能会有一些并发症未在您的示例数据中反映出来。

如果输入中有空行,则可能需要先删除它们:

set lines [lmap line $lines {if {[string is space $line]} continue else {set line}}]

如果某些块包含的行数少于或少于示例,则另一个简单模式是每个块都以一个具有可选(?)空格和单词Pattern的行开头。这些行(第一行除外)应在输出中以块分隔符开头:

set lines [split [string trim $list \n] \n]
puts [lindex $lines 0]
foreach line [lrange $lines 1 end] {
    if {[regexp {\s*Pattern} $line]} {
        puts \n$line
    } else {
        puts $line
    }
}
puts \n

如果这些行实际上不是以空格开头,则可以使用string match Pattern* $line而不是正则表达式。

文档:continueforeachiflindexlmaplmap替代,lrange,{{ 3}},putsregexpsetsplit

答案 3 :(得分:1)

% set list {    Pattern[1]: 
    Key : "key1" 
    Value : 100
    Pattern[2]: 
    Key : "key2" 
    Value : 20
    Pattern[3]: 
    Key : "key3" 
    Value : 30
    Pattern[4]: 
    Key : "key4" 
    Value : 220
}
% regexp -all -inline {Pattern\[\d+\].*?Value.*?\n} $list
{Pattern[1]: 
    Key : "key1" 
    Value : 100
    Pattern[2]: 
    Key : "key2" 
    Value : 20
    Pattern[3]: 
    Key : "key3" 
    Value : 30
    Pattern[4]: 
    Key : "key4" 
    Value : 220
}
% regexp -all -inline {Pattern\[\d+?\].*?Value.*?\n} $list   ;# only changing `\d+` to `\d+?`
{Pattern[1]: 
    Key : "key1" 
    Value : 100
} {Pattern[2]: 
    Key : "key2" 
    Value : 20
} {Pattern[3]: 
    Key : "key3" 
    Value : 30
} {Pattern[4]: 
    Key : "key4" 
    Value : 220
}

如果$ list 以换行符结尾,则您无法获得"模式[4]"元素返回。在这种情况下,请更改

% regexp -all -inline {Pattern\[\d+?\].*?Value.*?\n} $list

% regexp -all -inline {Pattern\[\d+?\].*?Value.*?(?:\n|$)} $list