相同的正则表达式在另一台机器上不匹配

时间:2014-02-16 18:18:41

标签: tcl expect

我有这个Tcl8.5代码:

set regexp_str {^[[:blank:]]*\[[[:blank:]]*[0-9]+\][[:blank:]]+0\.0\-([0-9]+\.[0-9]+) sec.+([0-9]+\.[0-9]+) ([MK]?)bits/sec[[:blank:]]*$}

set subject {
[  5]  0.0- 1.0 sec    680 KBytes  5.57 Mbits/sec
[  5]  0.0-150.0 sec    153 MBytes  8.56 Mbits/sec
[  4]  0.0- 1.0 sec  0.00 Bytes  0.00 bits/sec
[  4]  0.0-150.4 sec  38.6 MBytes  2.15 Mbits/sec
}
set matches [regexp -line -inline -all -- $regexp_str $subject]

$matches在一台机器上填充匹配的数据,而另一台只是获得一个空列表 两台机器都有Tcl8.5。

使用-about的{​​{1}}标记,返回以下列表:regexp

我不明白这怎么可能,我还应该做些什么来调试呢?


编辑#1,17 Feb 07:00 UTC:

@Donal Fellows:
“好”机器上的补丁级别是8.5.15 “坏”机器上的补丁级别是8.5.10。

我熟悉3 {REG_UUNPORT REG_ULOCALE}\s,但据我所知(请纠正我),它们都意味着更广泛的角色范围而不是我需要:
\d包含换行符,在我的示例中不得存在换行符 \s包含Unicode数字,在我的示例中我不会遇到这些数字 在正则表达式中,我通常希望尽可能具体,以避免我没有想到的情况。


有一些我没有说明的东西,可能很重要:
在shell中执行\d命令后,使用$subject变量填充变量expect_out(buffer)grep返回ssh会话的输出,该会话使用名为expect_out(buffer)的代理进行隧道传输(二进制名称为netcat):

nc

一般来说,收到的输出&在此会话中发送的只是ASCII /英文字符。 目标PC的提示包含spawn ssh -o "ProxyCommand nc %h %p" "$username@$ipAddress" ESC等控制字符,它们包含在BEL中。 我不认为这是一个问题因为我用所有这些字符测试了正则表达式并且它运行正常。

谢谢你们精心制作的信息!


编辑#2,17 Feb 11:05 UTC:

对@Donal研究员的回应:
的确,我试过了:

$subject

并得到(请忽略输出中的不同数字,想法是一样的)

set regexp_str {^[[:blank:]]*\[[[:blank:]]*[0-9]+\][[:blank:]]+0\.0\-([0-9]+\.[0-9]+) sec.+([0-9]+\.[0-9]+) ([MK]?)bits/sec[[:blank:]]*$}
puts [regexp -line -inline -all -- $regexp_str [string map {\r\n \n \r \n} $subject]]

另外,我尝试用{[ 5] 0.0-150.0 sec 86.7 MBytes 4.85 Mbits/sec} 150.0 4.85 M {[ 4] 0.0-150.8 sec 60.4 MBytes 3.36 Mbits/sec} 150.8 3.36 M 替换正则表达式字符串两边的[[:blank:]]

\s

它终于找到了我需要的东西:

set regexp_str {^\s*\[[[:blank:]]*[0-9]+\][[:blank:]]+0\.0\-([0-9]+\.[0-9]+) sec.+([0-9]+\.[0-9]+) ([MK]?)bits/sec\s*$}
puts [regexp -line -inline -all -- $regexp_str $subject]

3 个答案:

答案 0 :(得分:1)

(ETA:关于正则表达式的问题,为什么我要谈论将字符串按到列表中并从中挑选项目?请参阅本答案的结尾。)

作为一种解决方法,如果您不需要使用正则表达式,则此代码会给出完全相同的结果:

set result [list]
foreach line [split [string trim $subject] \n] {
    set list [string map {- { } / { }} $line]
    lappend result \
        $line \
        [lindex $list 3] \
        [lindex $list 7] \
        [string map {Mbits M Kbits K bits {}} [lindex $list 8]]
}

由于括号,这些行不是严格格式良好的列表,但确实有效。

澄清:

  • string trim命令在数据之前和之后取出换行符:否则会产生额外的空元素
  • split命令创建一个包含四个元素的列表,每个元素对应一行数据
  • foreach命令处理每个元素
  • string map命令将每个-/字符更改为空格,基本上使其成为(列表项目分隔符的一部分)
  • lappend逐行构建每行数据四个项目的结果列表:项目是整行,相应列表中的第四项,相应列表中的八项,以及第九项在string map命令将字符串MbitsKbitsbits缩短为MK后,在相应的列表中显示为空字符串,分别。

事情是(中等咆哮警告):正则表达式匹配不是字符串分析工具箱中唯一的工具,即使它有时看起来那样。除其他外,Tcl本身是一个强大的字符串和列表操作语言,通常比RE更具可读性。例如,还有scan:扫描表达式"[ %*d] %*f- %f sec %*f %*s %f %s"从数据字符串中捕获相关字段(假设它们被分成行并单独处理) - 剩下的就是查看最后捕获的字符串,以查看它是以MK还是其他内容(可能为b)开头。此代码与上面的解决方案和示例相同:

set result [list]
foreach line [split [string trim $subject] \n] {
    scan $line "\[ %*d\] %*f- %f sec %*f %*s %f %s" a b c
    lappend result $line $a $b [string map {its/sec {} Mb M Kb K b {}} $c]
}

正则表达式非常有用,但它们也很难正确并且在它们不正确时进行调试,即使你已经正确使用它们仍然难以阅读,并且从长远来看,维持。因为在很多情况下它们实际上是矫枉过正,所以至少考虑其他工具是否无法完成工作是有意义的。

答案 1 :(得分:1)

Tcl在所有平台上使用相同的正则表达式引擎。 (但仔细检查两台机器上是否有相同的patchlevel;这将让我们检查系统之间可能存在的确切代码更改(如果有的话)。)它也不应该是与换行终结者有关的任何事情; Tcl会在任何情况下自动将它们标准化,甚至远程类似于正常情况(特别是在脚本中)。

对于-about标志,只有3有用(它是捕获组的数量)。列表中的另一项是由RE编译器设置的关于RE的状态标志集,坦率地说它们仅对真正的 RE专家(和我们的测试套件)有用。我从来没有找到他们的用途!


您可以使用\s(助记符“空格”)而不是繁琐的[[:blank:]]\d(“数字”)代替[0-9]缩短您的RE。当我这样做时,我会得到更短的东西,更容易理解。

set regexp_str {^\s*\[\s*\d+\]\s+0\.0-(\d+\.\d+) sec.+(\d+\.\d+) ([MK]?)bits/sec\s*$}

它产生相同的匹配组。


[编辑]:即使您报告的代码的完全版本,直接从用于驱动8.5.10发行版的源代码存储库标记中检出,我也无法重现你的问题。 然而,它真的来自Expect缓冲区的事实真的很有帮助;问题实际上可能是线路分离序列不是换行符而是其他东西(CRLF - \r\n - 是1号嫌疑人,但是普通的回车也可能在那里)。由于各种原因,Expect肯定与普通I / O不同(特别是在终端处理中经常需要精确的字节序列)。

最简单的方法可能是在将字符串输入regexp之前手动标准化行分隔符。 (这不会影响缓冲区中的字符串;它会像往常一样使用Tcl进行复制。)

regexp -line -inline -all -- $regexp_str [string map {\r\n \n \r \n} $subject]

输出中还可能有其他不可见的字符。弄清楚真正正在进行的事情可能很复杂,但一般来说,你可以使用正则表达式来测试这个理论,看一下预期字符集的倒数是否匹配:

regexp {[^\n [:graph:]]} $subject

当我尝试粘贴的东西时,这不符合(好!)。如果它与您的真实缓冲区相反,它会为您提供一种解决问题的方法。

答案 2 :(得分:1)

我看到你在第一个破折号后面缺少可选空间。我插入了这些可选空格,一切正常:

set regexp_str {^[[:blank:]]*\[[[:blank:]]*[0-9]+\][[:blank:]]+0\.0\-[[:blank:]]*([0-9]+\.[0-9]+) sec.+([0-9]+\.[0-9]+) ([MK]?)bits/sec[[:blank:]]*$}
# missing -->                                                        ^^^^^^^^^^^^

set subject {
[  5]  0.0- 1.0 sec    680 KBytes  5.57 Mbits/sec
[  5]  0.0-150.0 sec    153 MBytes  8.56 Mbits/sec
[  4]  0.0- 1.0 sec  0.00 Bytes  0.00 bits/sec
[  4]  0.0-150.4 sec  38.6 MBytes  2.15 Mbits/sec
}   
set matches [regexp -line -inline -all -- $regexp_str $subject]

puts "\n\n"
foreach {all a b c} $matches {
    puts "- All: >$all<"
    puts "       >$a<"
    puts "       >$b<"
    puts "       >$c<"
}   

输出

- All: >    [  5]  0.0- 1.0 sec    680 KBytes  5.57 Mbits/sec<
       >1.0<
       >5.57<
       >M<
- All: >    [  5]  0.0-150.0 sec    153 MBytes  8.56 Mbits/sec<
       >150.0<
       >8.56<
       >M<
- All: >    [  4]  0.0- 1.0 sec  0.00 Bytes  0.00 bits/sec<
       >1.0<
       >0.00<
       ><
- All: >    [  4]  0.0-150.4 sec  38.6 MBytes  2.15 Mbits/sec<
       >150.4<
       >2.15<
       >M<

更新

在处理复杂的正则表达式时,我经常将表达式分解为几行并添加注释。以下内容相当于我之前的代码,但更详细,更容易进行故障排除。关键是使用regexp命令的附加标志:-expanded标志,告诉regexp忽略表达式中的任何空格和注释。

set regexp_str {
    # Initial blank
    ^[[:blank:]]*

    # Bracket, number, optional spaces, bracket
    \[[[:blank:]]*[0-9]+\]

    # Spaces
    [[:blank:]]+

    # Number, dash, number
    0\.0\-[[:blank:]]*([0-9]+\.[0-9]+)

    # Unwanted stuff
    [[:blank:]]sec.+

    # Final number, plus unit
    ([0-9]+\.[0-9]+)[[:blank:]]([MK]?)bits/sec

    # Trailing spaces
    [[:blank:]]*$
}   

set subject {
[  5]  0.0- 1.0 sec    680 KBytes  5.57 Mbits/sec
[  5]  0.0-150.0 sec    153 MBytes  8.56 Mbits/sec
[  4]  0.0- 1.0 sec  0.00 Bytes  0.00 bits/sec
[  4]  0.0-150.4 sec  38.6 MBytes  2.15 Mbits/sec
}   

set matches [regexp -expanded -line -inline -all -- $regexp_str $subject]

puts "\n\n"
foreach {all a b c} $matches {
    puts "- All: >$all<"
    puts "       >$a<"
    puts "       >$b<"
    puts "       >$c<"
}