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