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

时间:2019-12-17 02:57:46

标签: regex pcre

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

这个想法是查询一个包含时间戳和动作的日志文件。我想将日志文件中包含重复时间戳的任何行与包含在行中的“开始”字符串进行匹配。

使用此模式:

^(\b\d+)-(\d{2})-(\d{2}) (\d+):(\d{2})(?=\b[\s\S]*Starting\b)(?=[\s\S]*\b\1\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的重复值。

3 个答案:

答案 0 :(得分:1)

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)$“标志”是导入模式,这就是您要查找的内容,即“标志”。
    3. .*?在它们之间的中间是您不太在乎的字符。
  • 然后,它必须有另一行\n。此处,regexp使用标志gm。如果没有\n,它将停止检查以下行。

  • \k<ymdhm>意味着应用与最后一组“ ymdhm”相同的模式,这意味着下一个日志的时间应具有相同的数字。 Explanation代表\k

  • 然后懒惰地匹配任意字符。
  • 然后\k<flag>与上一个匹配的标志模式匹配。

答案 1 :(得分:1)

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

Give it a whirl

注意:

  • 这不能容忍间距上的偏差(您只需要用替换所有\s+即可)
  • 将仅匹配重复的块,而不是每个重复的单个块(在您的示例中,一个匹配项,包含两行)
  • 仅当顺序重复时才能识别重复项(这是为了保持正则表达式的效率)

我不同意@ggorlen的评估,对于需要这种表达能力的问题,正则表达式实际上是您可以做的最快的事情。


但是,如果您需要匹配不连续的“开始”行,但是则可以保证按顺序排列(基本上对于所有日志来说都是这样,同一分钟的“开始”行和非“开始”行都将彼此相邻),我们可以对此进行调整并仍然保持合理高效:

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

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

具有与b̲l̲o̲c̲k̲s̲匹配的相同警告,因此,匹配“起始”行之间的非起始行仍将匹配。


为了提高效率以使每行单独匹配,我们可以对这两个半部使用超前/后退。 我们需要复制正则表达式以捕获块的不同末端。

Some browsers wont even let you do crazy stuff like this,即使Chrome不能使所有在线测试人员都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\1\d{2}\.\d{3}\2\[\d+\][^\n]*)*\n\1\d{2}\.\d{3}\2\[\d+\]\3[^\n]*)|(?<=(?:^|\n)(\d{4}(?:-\d{2}){2} (?:\d{2}:){2})\d{2}\.\d{3}( [+-]\d{2}:\d{2} )\[\d+\]( - Starting)[^\n]*(?:\n\4\d{2}\.\d{3}\5\[\d+\][^\n]*)*)\n\4\d{2}\.\d{3}\5\[\d+\]\6[^\n]*

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

(
    ([regex](
        (
            '(?:^|\n)(\d{4}(?:-\d{2}){2} (?:\d{2}:){2})\d{2}\.\d{3}( [+-]\d{2}:\d{2} )\[\d+\]( - Starting)[^\n]*(?=(?:\n\1\d{2}\.\d{3}\2\[\d+\][^\n]*)*\n\1\d{2}\.\d{3}\2\[\d+\]\3[^\n]*)',
            '(?<=(?:^|\n)(\d{4}(?:-\d{2}){2} (?:\d{2}:){2})\d{2}\.\d{3}( [+-]\d{2}:\d{2} )\[\d+\]( - Starting)[^\n]*(?:\n\4\d{2}\.\d{3}\5\[\d+\][^\n]*)*)\n\4\d{2}\.\d{3}\5\[\d+\]\6[^\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

enter image description here

答案 2 :(得分:1)

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

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

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

Regex demo