为什么我的tcl正则表达式与perl相比表现如此糟糕?

时间:2013-08-13 12:03:50

标签: regex performance perl tcl

set fr [open "x.txt" r]
set fw [open "y.txt" w]
set myRegex {^([0-9]+) ([0-9:]+\.[0-9]+).* ABC\.([a-zA-Z]+)\[([0-9]+)\] DEF\(([a-zA-Z]+)\) HIJ\(([0-9]+)\) KLM\(([0-9\.]+)\) NOP\(([0-9]+)\) QRS\(([0-9]+)\)}
while { [gets $fr line] >= 0 } {
   if { [regexp $myRegex $line match x y w z]} {
       if { [expr $D >> 32] == [lindex $argv 0]} {
         puts $fw "$x"
       }
   }
}
close $fr $fw

上面的tcl代码需要永远(32秒或更长时间)才能执行。在perl中执行基本相同的操作在3秒或更短时间内运行。我知道perl对于一些正则表达式表现更好,但相比之下tcl性能真的会这么糟糕吗?差了10多倍?

我正在使用TCL 8.4

以下是使用正则表达式运行上述代码的指标以及相同正则表达式的缩减版本

32s is the time taken for the above code to execute
22s after removing: QRS\(([0-9]+)\) 
17s after removing: NOP\(([0-9]+)\) QRS\(([0-9]+)\)
13s after removing: KLM\(([0-9\.]+)\) NOP\(([0-9]+)\) QRS\(([0-9]+)\)
9s  after removing: HIJ\(([0-9]+)\) KLM\(([0-9\.]+)\) NOP\(([0-9]+)\) QRS\(([0-9]+)\)
6s  after removing: DEF\(([a-zA-Z]+)\) HIJ\(([0-9]+)\) KLM\(([0-9\.]+)\) NOP\(([0-9]+)\) QRS\(([0-9]+)\)}

1 个答案:

答案 0 :(得分:6)

问题是你在RE中有很多捕捉和回溯;特定组合与Tcl RE引擎的效果不佳。在一个层面上的原因是Tcl使用完全不同类型的RE引擎到Perl(虽然它对其他RE更好;这个区域非常重要)。

如果可以,请从RE:

中删除早期的.*
^([0-9]+) ([0-9:]+\.[0-9]+).* ABC\.([a-zA-Z]+)\[([0-9]+)\] DEF\(([a-zA-Z]+)\) HIJ\(([0-9]+)\) KLM\(([0-9\.]+)\) NOP\(([0-9]+)\) QRS\(([0-9]+)\)
                           ^^

这是麻烦的真正原因。替换为更精确的东西,例如:

(?:[^A]|A[^B]|AB[^C])*

此外,将RE中的捕获组数量减少到您需要的数量。您可以将整个代码转换为:

set fr [open "x.txt" r]
set fw [open "y.txt" w]
set myRegex {^([0-9]+) (?:[0-9:]+\.[0-9]+)(?:[^A]|A[^B]|AB[^C])* ABC\.(?:[a-zA-Z]+)\[([0-9]+)\] DEF\((?:[a-zA-Z]+)\) HIJ\((?:[0-9]+)\) KLM\((?:[0-9\.]+)\) NOP\((?:[0-9]+)\) QRS\((?:[0-9]+)\)}
while { [gets $fr line] >= 0 } {
    # I've combined the [if]s and the [expr]
    if { [regexp $myRegex $line -> A D] && $D >> 32 == [lindex $argv 0]} {
        puts $fw "$A"
    }
}
close $fr $fw

另请注意,if { [expr ...] }是一种可疑的代码气味,就像任何未支撑的表达一样。 (在非常特殊的情况下,它有时是必要的,但几乎总是表明代码过于复杂。)