【发布时间】:2020-01-30 08:42:08
【问题描述】:
注意
@Toto 接受的答案最终在现实世界中在具有许多可能匹配项的大文件上效果更好,但是,如果您在任何大小的文件上使用类似的正则表达式,只有几个可能的匹配项,那么 @Pavel Lint 的答案会很好,可能会更快,尽管我没有测试过速度。请记住,此处的差异以毫秒为单位,因此实际上差别不大,除非您连续多次执行此操作。
我有以下字符串,它来自 SQL 错误日志文件的一部分(为清楚起见进行了修改):
2020-01-27 11:12:00.72 备份日志已备份。数据库:ReportServerTempDB,创建日期(时间):2019/05/22(12:31:06),第一个LSN:79:1911:1,最后一个LSN:79:1933:1,转储设备数量:1,设备信息:(文件=1,类型=磁盘:{'E:\SQLLogDumps\ReportServerTempDB_tlog_20200127111200.trn'})。这只是一条信息性消息。无需用户操作。
2020-01-27 11:21:47.95 登录错误:17806,严重性:20,状态:14。
2020-01-27 11:21:47.95 登录次数一
2020-01-27 11:21:47.95 登录错误:18452,严重性:14,状态:1。
2020-01-27 11:21:47.95 登录 登录失败。登录来自不受信任的域,不能与集成身份验证一起使用。 [客户端:192.168.4.208]
2020-01-27 11:21:47.95 登录错误:17806,严重性:20,状态:14。
2020-01-27 11:21:47.95 登录次数二
2020-01-27 11:21:47.95 登录错误:18452,严重性:14,状态:1。
2020-01-27 11:21:47.95 登录 登录失败。登录来自不受信任的域,不能与集成身份验证一起使用。 [客户:192.168.4.208]
上面的粗体字是我目前所写的表达式所匹配的内容。
我想找到包含“严重性:20”的行加上以下行的 last 出现,也就是第二个粗体部分。然而,在野外,一个文档中可能出现任意数量的事件。
我在 regex101.com 上玩过,并在 regular-expressions.info 上阅读了很多内容,但无法制作有效的正则表达式。
我目前的表达是:^.*Severity: 20.*\R^.*(?!(^.*\R)*Severity: 20)
我还在 Google 上进行了很多探索,但找不到任何有助于匹配跨越多行且属于长文档一部分的重复模式。
是否可以只匹配模式的最后一次出现?如果有,怎么做?
为了澄清,我正在寻找纯正则表达式。我知道这在 Python 中是可能的,方法是将所有匹配项作为列表返回,然后获取列表的最后一个元素,但我没有那个选项。另外,我正在使用 Notepad++ 正则表达式搜索,我相信它使用 Boost 版本的正则表达式。
更新
两个给定的答案都可以在测试字符串上完美运行,但是在大型/真实文件上的结果非常有趣,有关更多详细信息,请参见下文。
根据上面的测试字符串,我对@Pavel Lint 和@Toto 的答案进行了基准测试。 @Pavel Lint 的回答要快得多,大约是毫秒的 10 倍。
@Pavel Lint^.+Severity: 20.*\R^.*\R(?![\s\S]*Severity: 20)
1588步,平均2ms
@Toto[\s\S]+\K^.*?Severity: 20.*\R.*
61955步,平均36ms
但是,实际文件的结果有很大不同。现在,我无法提供我用于测试的实际文件,因为它是客户端的日志文件,所以我不会责怪@Pavel Lint 的答案,因为它在测试字符串上工作得非常好。我的问题可能不够清楚,关于这需要在野外做什么,但我确实声明“在野外,一个文档中可能有任何次出现”,我相信这涵盖了我们希望避免灾难性回溯的场景。
测试文件有 26,192 行,出现 1595 次“严重性:20”错误(与测试字符串中显示的模式匹配,但第二行中的消息会有所不同,可能包含任何单词/数字/特殊字符)。
上面的第一个正则表达式来自@Pavel Lint,当我运行它时杀死了 Notepad++。
我的半受过教育的猜测是发生了灾难性的回溯。
来自@Toto 的第二个正则表达式几乎立即与最后一次匹配。
最终,@Toto 的回答对于小样本来说速度较慢,但可以扩展到具有许多可能匹配项的非常大的文件。
【问题讨论】:
-
在 notepad++ 上,您可以简单地将搜索方向更改为“向上”,然后从末尾开始搜索下一个结果。无论搜索结果如何,都将是最后一个匹配项。
-
我刚刚看了看,选择“正则表达式”选项将搜索方向限制为向下。是的,我知道我可以寻找像“严重性:20”这样简单的东西而不使用正则表达式,但如果可能的话,我想使用正则表达式。
-
你用的是什么版本?我在 v7.7.1 上,可以向后搜索就好了
-
啊,我明白了,由于其他原因(使用“Python 脚本”插件),我使用的是 7.3.3。我想它是后来添加的。
-
如果你不能升级,那么你就不走运了。正则表达式没有“第一个”或“最后一个”的概念。另一种方法是使用“在当前文档中查找全部”,然后单击出现的窗口的最后一行。或者您可以打开“
.匹配换行符”搜索.+<rest of the pattern>,匹配的结尾将是您要查找的行。