如何匹配多行中的重复值?
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 的重复值。
(?<log>(?<ymdhm>\d{4}-\d{2}-\d{2} \d{2}:\d{2}).*?(?<flag>Starting)$)\n\k<ymdhm>.*?\k<flag>
[更新]
好的,我更新了正则表达式,这并不像我预期的那样容易。
解释如下:
组 "log" 根据您的基本规则匹配单行。它有几个部分:
(?<ymdhm>\d{4}-\d{2}-\d{2} \d{2}:\d{2})
"ymdhm" YY-MM-DD HH:MM,这是为以后的匹配导入的,因为你关心时间数字直到分钟,下一个符合条件的行必须具有完全相同的模式像这个。
(?<flag>Starting)$
"flag" 是导入模式,这就是您要查找的内容,即 "flag"。
.*?
中间是你不太关心的角色。
那么,肯定还有一行\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]*)+
注:
- 这不能容忍间距偏差(您只需将所有
</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行
我正在尝试匹配具有重复值的多行。如果有多个匹配项,我的脚本将继续。我一直在浏览反向引用文档,但我似乎无法正确处理我的情况。
想法是查询包含时间戳和操作的日志文件。我想将日志文件中包含重复时间戳的任何行与行中包含的 "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 的重复值。
(?<log>(?<ymdhm>\d{4}-\d{2}-\d{2} \d{2}:\d{2}).*?(?<flag>Starting)$)\n\k<ymdhm>.*?\k<flag>
[更新]
好的,我更新了正则表达式,这并不像我预期的那样容易。
解释如下:
组 "log" 根据您的基本规则匹配单行。它有几个部分:
(?<ymdhm>\d{4}-\d{2}-\d{2} \d{2}:\d{2})
"ymdhm" YY-MM-DD HH:MM,这是为以后的匹配导入的,因为你关心时间数字直到分钟,下一个符合条件的行必须具有完全相同的模式像这个。(?<flag>Starting)$
"flag" 是导入模式,这就是您要查找的内容,即 "flag"。.*?
中间是你不太关心的角色。
那么,肯定还有一行
\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]*)+
注:
- 这不能容忍间距偏差(您只需将所有
</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行