【问题标题】:Match a pattern only if between an opening and closing pattern仅在打开模式和关闭模式之间匹配模式
【发布时间】:2014-09-19 22:02:59
【问题描述】:

我的正则表达式:

(?si)\bStart\b(.*?)\bError\b(.*?)\bEnd\b

这适用于以下场景:

stuff happens  
Start  
stuff happens  
Error  
stuff happens  
End

但也匹配Error之外的StartEnd序列:

Start  
End  
Error  
Start  
End

当条件变成场景 #2 时,如何匹配第一个示例中的命中?

【问题讨论】:

    标签: regex pcre


    【解决方案1】:

    Alexander's answer 可能已经足够好了,但我会这样做:

    (?si)\bStart\b(?:(?!\b(?:Start|End)\b).)*\bError\b(?:(?!\b(?:Start|End)\b).)*\bEnd\b
    

    这个正则表达式的主要优点是它失败得更快。 ((?!\bStart\b).)*? 工作正常,如果有一个 End 你期望一个,但如果没有匹配是可能的,它仍然必须一直到下一个 Start (如果有)或到末尾文件才可以放弃匹配。

    事实上,您可以更进一步,完全消除回溯:

    (?si)\bStart\b(?>(?:(?!\b(?:Start|End|Error)\b).)*)\bError\b(?>(?:(?!\b(?:Start|End|Error)\b).)*)\bEnd\b
    

    添加一个Error 替代项并将该部分包含在一个原子组中意味着如果它找到一个Start 并且没有在下一个End 之前找到一个Error,它会失败马上。

    这是一个 PowerShell 示例(由 RegexBuddy 生成):

    $regex = [regex] '(?si)\bStart\b(?>(?:(?!\b(?:Start|End|Error)\b).)*)\bError\b(?>(?:(?!\b(?:Start|End|Error)\b).)*)\bEnd\b'
    $matchdetails = $regex.Match($subject)
    while ($matchdetails.Success) {
        # matched text: $matchdetails.Value
        # match start: $matchdetails.Index
        # match length: $matchdetails.Length
        $matchdetails = $matchdetails.NextMatch()
    }
    

    更新:我刚刚意识到我不应该将Error 分支添加到第二个替代项中。我的正则表达式仅匹配包含 Error 一次的 Start..End 块,这可能过于具体。此版本匹配一个至少出现一次Error的块:

    (?si)\bStart\b(?>(?:(?!\b(?:Start|End|Error)\b).)*)\bError\b(?>(?:(?!\b(?:Start|End)\b).)*)\bEnd\b
    

    【讨论】:

      【解决方案2】:

      PowerShell,使用负前瞻并假设“事情发生”部分从不包含单词“开始”或“结束”:

      $txt = Get-Content file.txt | Out-String
      $pattern = "(?si)\bStart\b((?!\bEnd\b).)*?\bError\b((?!\bStart\b).)*?\bEnd\b"
      $regex = New-Object System.Text.RegularExpressions.Regex($pattern)
      $regex.Matches($txt)
      

      解释here

      【讨论】:

        【解决方案3】:

        好的,所以在我能够理解所接受的答案是逐步完成的内容之后,我回到了这个问题,这对我来说比只在一行中的所有内容更容易理解。这个替代答案解释了从开始到结束的过程,原始问题的目标是按顺序采用 3 匹配模式并确保不会发生不希望的匹配。

        第 1 步:在添加排除项之前让您的模式正常工作

        \bStart\b.*\bError\b.*\bEnd\b
        

        第 2 步:放置非捕获组以检查任何类型的字符,同时在其中移动 .。这些非捕获组 (?:.) 现在只是占位符,意味着它们会检查 任何 字符,因此不会破坏我们已经建立的模式。

        \bStart\b(?:.)*\bError\b(?:.)*\bEnd\b
        

        第 3 步:现在我们希望将这些非捕获组包含在一个正向前瞻中,在它的非捕获组内部有一个负前瞻,这样我们就知道如果它检测到 Start、End、或除最后一个错误之外的任何内容。在不破坏最小匹配功能的情况下,我们无法真正分解这部分内容。

        \bStart\b(?>(?:(?!\b(Start|End|Error)\b).))*\bError\b(?>(?:(?!\b(Start|End)\b).))*\bEnd\b
        

        第 4 步:现在,只需在开头添加行匹配过滤器,就可以开始了!

        (?si)\bStart\b(?>(?:(?!\b(Start|End|Error)\b).))*\bError\b(?>(?:(?!\b(Start|End)\b).))*\bEnd\b
        

        我是一个高度视觉化的学习者,所以我分享了一张个人帮助我分解它的图形。

        【讨论】:

          猜你喜欢
          • 2021-09-12
          • 1970-01-01
          • 1970-01-01
          • 2021-09-10
          • 1970-01-01
          • 1970-01-01
          • 2011-07-07
          • 2020-02-09
          • 1970-01-01
          相关资源
          最近更新 更多