如何匹配多行中的重复值?

How do you match duplicate values on multiple lines?

我正在尝试匹配具有重复值的多行。如果有多个匹配项,我的脚本将继续。我一直在浏览反向引用文档,但我似乎无法正确处理我的情况。

想法是查询包含时间戳和操作的日志文件。我想将日志文件中包含重复时间戳的任何行与行中包含的 "Starting" 字符串相匹配。

使用此模式:

^(\b\d+)-(\d{2})-(\d{2}) (\d+):(\d{2})(?=\b[\s\S]*Starting\b)(?=[\s\S]*\b\b)

我希望匹配前两行,因为时间戳完全相同。

2019-10-31 05:49:52.416 +10:00 [1] - Starting
2019-10-31 05:49:53.416 +10:00 [1] - Starting
2019-10-31 06:53:58.416 +10:00 [1] - Starting

目前,它只捕获第一行(1 个匹配项)。我如何让它匹配多行的重复值?

编辑:

为澄清起见,我的模式正在查找 YYYY-MM-DD HH:MM 的重复值。

The example

(?<log>(?<ymdhm>\d{4}-\d{2}-\d{2} \d{2}:\d{2}).*?(?<flag>Starting)$)\n\k<ymdhm>.*?\k<flag>

[更新]
好的,我更新了正则表达式,这并不像我预期的那样容易。

解释如下:

  • 组 "log" 根据您的基本规则匹配单行。它有几个部分:

    1. (?<ymdhm>\d{4}-\d{2}-\d{2} \d{2}:\d{2}) "ymdhm" YY-MM-DD HH:MM,这是为以后的匹配导入的,因为你关心时间数字直到分钟,下一个符合条件的行必须具有完全相同的模式像这个。
    2. (?<flag>Starting)$ "flag" 是导入模式,这就是您要查找的内容,即 "flag"。
    3. .*?中间是你不太关心的角色。
  • 那么,肯定还有一行\n。这里的正则表达式使用标志 gm。没有 \n 它将停止检查以下行。

  • \k<ymdhm> 表示应用与上一组相同的模式 "ymdhm",这意味着下一个日志的时间应该具有相同的数字。 Explanation 对于 \k.

  • 然后惰性匹配任意字符。
  • 然后\k<flag>匹配上次匹配的标志模式。
(?:^|\n)(\d{4}(?:-\d{2}){2} (?:\d{2}:){2})\d{2}\.\d{3}( [+-]\d{2}:\d{2} )\[\d+\]( - Starting)[^\n]*(\n\d{2}\.\d{3}\[\d+\][^\n]*)+

Give it a whirl

注:

  • 这不能容忍间距偏差(您只需将所有 </code> 替换为 <code>\s+s)
  • 将只匹配重复块,而不是单独匹配每个重复项(在您的示例中匹配一个,包含两行)
  • 只有连续的重复才会被识别(这是为了保持正则表达式的效率)

我不同意@ggorlen 的评估,对于需要这种表达能力.

的问题,正则表达式确实是最快的处理方法

但是,如果您需要 匹配 "Starting" 不连续的行,但是 您可以 保证行将是有序的(对于基本上所有的日志都是如此,同一分钟的"Starting"和非"starting"行都将紧挨着彼此)我们可以容纳它并仍然保持它合理高效:

(?:^|\n)(\d{4}(?:-\d{2}){2} (?:\d{2}:){2})\d{2}\.\d{3}( [+-]\d{2}:\d{2} )\[\d+\]( - Starting)[^\n]*(?:\n\d{2}\.\d{3}\[\d+\][^\n]*)*\n\d{2}\.\d{3}\[\d+\][^\n]*

Have a play 以确保它能满足您的需求

这与它匹配 b̲l̲o̲c̲k̲s̲ 的警告相同,因此匹配 "starting" 行之间的非起始行将仍然匹配。


牺牲效率以获得每行的个人匹配,我们可以使用 lookahead/behinds 两半。
我们需要复制正则表达式以捕获块的不同端。

Some browsers wont even let you do crazy stuff like this, and even though Chrome can none of the online testers would let me give you a breakdown of the resulting regex

(?:^|\n)(\d{4}(?:-\d{2}){2} (?:\d{2}:){2})\d{2}\.\d{3}( [+-]\d{2}:\d{2} )\[\d+\]( - Starting)[^\n]*(?=(?:\n\d{2}\.\d{3}\[\d+\][^\n]*)*\n\d{2}\.\d{3}\[\d+\][^\n]*)|(?<=(?:^|\n)(\d{4}(?:-\d{2}){2} (?:\d{2}:){2})\d{2}\.\d{3}( [+-]\d{2}:\d{2} )\[\d+\]( - Starting)[^\n]*(?:\n\d{2}\.\d{3}\[\d+\][^\n]*)*)\n\d{2}\.\d{3}\[\d+\][^\n]*

幸运的是,PowerShell(正如您在评论中提到的那样)仍然可以很好地处理它,但我敢肯定它会在处理大型日志文件时停止运行。

(
    ([regex](
        (
            '(?:^|\n)(\d{4}(?:-\d{2}){2} (?:\d{2}:){2})\d{2}\.\d{3}( [+-]\d{2}:\d{2} )\[\d+\]( - Starting)[^\n]*(?=(?:\n\d{2}\.\d{3}\[\d+\][^\n]*)*\n\d{2}\.\d{3}\[\d+\][^\n]*)',
            '(?<=(?:^|\n)(\d{4}(?:-\d{2}){2} (?:\d{2}:){2})\d{2}\.\d{3}( [+-]\d{2}:\d{2} )\[\d+\]( - Starting)[^\n]*(?:\n\d{2}\.\d{3}\[\d+\][^\n]*)*)\n\d{2}\.\d{3}\[\d+\][^\n]*'
        ) -join '|')
    ).Matches((
        '2019-10-31 05:49:52.416 +10:00 [1] - Starting',
        '2019-10-31 05:49:53.416 +10:00 [2] - not starting',
        '2019-10-31 05:49:53.416 +10:00 [2] - Starting',
        '2019-10-31 05:49:53.416 +10:00 [3] - Starting',
        '2019-10-31 06:53:58.416 +10:00 [1] - Starting',
        '2019-10-31 06:53:58.416 +10:00 [1] - Identical but not "starting"'
    ) -join "`n")
).Value

您可以通过捕获您认为在以下所有行中相同的时间戳部分来使用 backreference,并且您还可以捕获 Starting 的部分第二个捕获组。

然后您可以重复匹配以与第 1 组相同的值开头并包含第 2 组的所有行。

^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}).*(\bStarting\b)(?:\R.+)+
  • ^ 行首
  • ( 捕获 组 1
    • \d{4}-\d{2}-\d{2} \d{2}:\d{2} 匹配您要捕获的时间戳格式
  • ) 关闭群组
  • .+ 匹配除换行符以外的任何字符 1+ 次
  • ( 捕获 第 2 组
    • \bStarting\b 匹配从单词边界开始
  • ) 关闭群组
  • (?:非捕获组
    • \R.+ 匹配 Unicode 换行符序列,对第 1 组中捕获的内容的反向引用,除了换行符之外的任何字符的 1+ 次以及对第 2 组中捕获的内容的反向引用
  • )+关闭非捕获组并重复1+次以匹配至少2行

Regex demo