【问题标题】:Matching a regexp in TCL PERL匹配 TCL PERL 中的正则表达式
【发布时间】:2016-03-27 06:34:52
【问题描述】:

我有以下模式

    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 。我正在使用的正则表达式无法解决目的

set updateList [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

【问题讨论】:

  • 或许,Pattern\[\d+\]:\s*Key.*\s*Value.*
  • @WiktorStribiżew:它不工作
  • set updateList [regexp -all -inline {Pattern\[[0-9]+\]:\s*Key[^\n]*\s*Value[^\n]*} $str] 是。一个点与 Tcl 中的换行符匹配,这就是上面那个不起作用的原因。
  • 查看 Wiktor 的回答:您只需将 \d+ 更改为 \d+?
  • @glennjackman:差不多。

标签: regex tcl


【解决方案1】:

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

因此,根据您的正则表达式,您可以\d+ 设为惰性,将\d+? 替换为\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

【讨论】:

  • 这里是 another, shorter, demo 证明了我的观点,即你的正则表达式中的“从惰性到贪婪”的“转换”:\d+[a-zA-Z]+?\d+? 应该匹配 56gddd666 中的 56gddd6,但它匹配整个字符串,因为最后一个 \d+? 实际上是一个贪婪的子模式(它继承自作为贪婪子模式的 [a-zA-Z]+?,因为它继承了第一个 \d+ 的行为)。
  • 很好地解释了模式的贪心选择。鉴于此,将 \d+ 更改为 \d+? 将适用于 OP,因为所有其他量词也是非贪婪的。
  • @glennjackman:我也想过这一点,但还有一个警告:最后一项后面没有换行符。需要一个交替组。
【解决方案2】:

试试这个

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

. 字符匹配除换行符以外的任何字符,因此您需要添加它。请注意,您的行可能以回车符结尾,因此您可能需要添加 \ r 在。

【讨论】:

    【解决方案3】:
    % 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 以换行符结尾,则不会返回“pattern[4]”元素。在这种情况下,改变

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

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

    【讨论】:

      【解决方案4】:

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

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

      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 替换、lrangeputsregexpsplitstring

      【讨论】:

      • 我喜欢。您假设(合理地)键值中没有换行符,或任何“意外”空白行。
      • 添加此代码如何回答问题的说明将改善您对未来访问者的回答(此回答被标记为低质量)。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-08-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多