我有一个相当复杂的regex搜索,最近我不得不将其扩展到"反向"模式.这很容易实现,但性能大约下降了10倍.

如果有任何关于如何改善这个问题的建议,我将不胜感激,但我对如何改进并不挑剔.例如,如果这比单个非常复杂的步骤快,那么可以使用两个或多个步骤.

Original pattern

import regex

expression = regex.compile(
    fr"""
(?:{CUE})  # cue before targets. non capture (?:)
(?:{PADDING})  # text before the match.
(?:
(?:{ITEM_SEPARATION})?  # none or once
(?P<targets>{targets_as_regex})
)+
""",
    regex.VERBOSE,
)

需要知道:CUE是一个很短的选项列表,类似于:

CUE = r"""word
|two\swords
|simple\soptions?
"""

这大约是5-15个选项,非常简单(对于相反的场景来说要简单一些).

PADDING实际上是任何东西,但不超过150.(PADDING = r".{0,150}?").在相反的情况下,限制性更强一些,只有[,A-Za-z\d\s]

ITEM_SEPARATION很简单:可选空格后跟逗号、有界"y"和更多可选空格:

ITEM_SEPARATION = r"""
\s*          # optional spaces
(?:,|\by\b)  # non-capture, either comma, or 'y' (with bounds) 
\s*          # optional spaces
"""

最后,问题可能出现在targets_as_regex中,这是一个有界名称的大列表.这很容易有数百或数千个选项,尽管每个调用都会传递一个唯一的目标列表.

其目的是匹配以下内容:

这是一个文本,带有cue,然后多一点文本和matchother match y final match.在"."之后我们停止匹配.

而且它的性能足够好.但情况正好相反:

Reversed pattern

这样做的目的是:

这不是一场比赛,因为停了下来.这是matchanother matchyfinal match,因为现在我们找到了cue.等

我自然地回收了我的图案:

expression = regex.compile(
    fr"""
(
(?P<targets>{targets_as_regex})
(?:{ITEM_SEPARATION})?  
)+
(?:{SIMPLE_PADDING})  # text before the match
(?:{CUE_AFTER})  # cue after targets
""",
    regex.VERBOSE,
)

它按预期工作,但当目标(很多)出现在线索(很少)之前时,性能非常差.

What I tried

我添加了一个基本判断,如果提示根本不在文本中,我们会忽略整个内容,但这并没有多大帮助.我测试过的另一个 idea 是搜索所有匹配的目标(不考虑提示和其他),然后将仅包含这些匹配的简短列表(targets_as_regex)传递给慢速模式.这实际上有所帮助,但没有多大帮助(性能仍接近x10的下降).

还有其他的 idea 吗?

推荐答案

((?P<targets>{targets_as_regex})(?:{ITEM_SEPARATION})?)+是众所周知的catastrophic backtracking俯卧模式(类似于(a+b?)+,减少到(a+)+),模式越向左,越危险.

您需要将(a+b?)+这样的模式"展开"为a+(?:ba+)*,使后续子模式仅在字符串中的不同位置匹配.

您需要的模式是

fr"""(?:{targets_as_regex})(?:{ITEM_SEPARATION}(?:{targets_as_regex}))*(?:{SIMPLE_PADDING})(?:{CUE_AFTER})"""

为了加快速度,需要从targets_as_regex构建一个regex-trie,请参见Speed up millions of regex replacements in Python 3.

Python相关问答推荐

如何使用symy打印方程?

Python 约束无法解决n皇后之谜

将两只Pandas rame乘以指数

删除字符串中第一次出现单词后的所有内容

给定高度约束的旋转角解析求解

如何根据一列的值有条件地 Select 前N组?

计算分布的标准差

如何更新pandas DataFrame上列标题的de值?

为什么\b在这个正则表达式中不解释为反斜杠

为什么if2/if3会提供两种不同的输出?

我对这个简单的异步者的例子有什么错误的理解吗?

将链中的矩阵乘法应用于多组值

Python Mercury离线安装

python的文件. truncate()意外地没有截断'

对数据帧进行分组,并按组间等概率抽样n行

如何在Polars中处理用户自定义函数的多行结果?

关于数字S种子序列内部工作原理的困惑

有什么方法可以在不对多索引DataFrame的列进行排序的情况下避免词法排序警告吗?

如何在微调Whisper模型时更改数据集?

Python键盘模块不会立即检测到按键