使用python和javascript的正则表达式速度慢,但在go和php中很快就会失败

使用python和javascript的正则表达式速度慢,但在go和php中很快就会失败,javascript,python,regex,go,Javascript,Python,Regex,Go,我编写了一个正则表达式来解析PostgreSQL错误,试图向用户显示哪个字段有重复的数据。 正则表达式是这样的: ^DETAIL:.[^\(]+.(.[^\)]+).[^\(]+.(.[^\)]+). already exists 如果您针对这样的正确消息运行它,它将非常快(): 但是,如果PostgreSQL发出另一条类似于以下消息的消息,那么python在我的机器上大约需要30秒才能回答() 如果转到regex101链接,您可以看到,如果切换到不同的语言,如php或go,它们都会很快返回,

我编写了一个正则表达式来解析PostgreSQL错误,试图向用户显示哪个字段有重复的数据。 正则表达式是这样的:

^DETAIL:.[^\(]+.(.[^\)]+).[^\(]+.(.[^\)]+). already exists
如果您针对这样的正确消息运行它,它将非常快():

但是,如果PostgreSQL发出另一条类似于以下消息的消息,那么python在我的机器上大约需要30秒才能回答()

如果转到regex101链接,您可以看到,如果切换到不同的语言,如php或go,它们都会很快返回,表示未找到匹配项,但如果选择python或javascript,则会出现超时

我的快速修复是这样的:

match = 'already exists' in error_message and compiled_regex.search(error_message)
你认为是什么原因造成的?在我获得所需数据之前,可能是贪婪的运营商在消费吗

更新1

在python中使用re.IGNORECASE会使它慢9秒左右,因为它花费了太多的时间来小写

无知的

无故

更新2

玩游戏时,我看到,要让它工作起来,但失败了,一个简单的改变就会失败

^DETAIL:.[^\(]+?\((.[^\)]+?).[^\(]+?.(.[^\)]+?). already exists
                            ^ just changing this to \) make it stop timing out
^DETAIL:.[^\(]+?\((.[^\)]+?)\)[^\(]+?.(.[^\)]+?). already exists
这是一个正则表达式怪物:)

为什么不拆分这两个正则表达式呢

  • 检查
    是否已存在
    是否匹配(非常快)
  • 提取要使用现有正则表达式显示的数据
    ^DET.[^\([^\]+。([^\]+).[^\(]+。([^\)]+).[^\]+)
  • 这将大大加快您的代码速度。(你甚至可以像我一样缩短细节)

    包regexp实现正则表达式搜索

    所接受的正则表达式的语法与常规的相同 Perl、Python和其他语言使用的语法。更准确地说,它 RE2是否接受并在中描述了语法 ,但\C.除外。有关 语法,运行

    go doc regexp/syntax
    
    此包提供的regexp实现保证 输入的大小与运行时间呈线性关系。(这是一处不属于 由大多数常规的开源实现保证 表达式。)有关此属性的详细信息,请参阅

    或者任何关于自动机理论的书



    通过设计,Go正则表达式可以保证在输入大小为线性的时间内运行,这是正则表达式的一些其他实现无法保证的特性。请看。

    这不是问题的真正答案,但我认为问题可能在于贪婪的运算符。不管怎样,我认为你应该让它的一部分懒散地快速失败

    我使用了这种模式,在regex101上的所有语言引擎上都可以:

    ^DETAIL:.+?\((.+)\).+?\((.+)\) already exists.
    


    TL;DR:使用以下方法:

    ^详细信息:\s*+键[^\(]+\(.+)\)[^\(]+\([^\)]+\)已存在

    看到和

    解释:

    首先,原始regexp似乎与整个键组不匹配,您在
    下(internal_name::text
    )停止,遗漏了复合键的一些列以及不平衡的括号。如果您这样修改它,应该可以捕获复合键。如果不应该这样做,请告诉我:

    ^详细信息:.[^\(]+。(.+)\)[^\(]+。(.[^\)]+)。已存在

    仅仅通过改变这一点,正则表达式是“可运行的”,但仍然相当缓慢

    主要原因之一是
    [^\(]+
    。它首先匹配到
    详细信息:失败的行包含(空格)
    并与正则表达式的其余部分继续。它将不匹配,因此它将回溯到少一个字符,直到
    详细信息:失败的行包含
    ,并继续与正则表达式的其余部分匹配。它将不匹配,因此将返回到
    详细信息:失败的行包含
    …等

    避免这种情况的一种方法是使用所有格量词。这意味着一旦你取了东西,你就不能再回去了。因此,使用这个
    [^”(]++
    而不是这个
    [^”(]++
    (也就是说:
    ^DETAIL:.[^(.+)[^+.[^+.[^+.]+.[^+.]已经存在的
    )可以使regexp的步数从28590减少到1290

    但是您仍然可以改进它。如果您知道所需的数据使用关键字
    key
    ,请使用它!这样,由于失败的示例中没有该关键字,它将使正则表达式很快失败(一旦它读取了详细信息和下一个单词)

    因此,如果使用
    ^详细信息:\s*+键[^\(]+.(.+)\)[^\(]+.[^\]+)。已经存在的步骤现在只有12个

    如果您觉得使用
    过于具体,可以使用一些不太通用的方法来查找“not'Fail'”。如下所示:

    ^详细信息:\s*+(?!Fail)[^\(]+。(.+)\)[^\(]+。(..[^\)]+)。已存在

    这样走17步

    最后,您可以针对匹配的内容调整正则表达式

    更改此项:

    ^DETAIL:\s*+Key[^\(]++.(.+)\)[^\(]+
    .           # <============= here, use \( instead
    (.[^\)]+). already exists
    
    ^详细信息:\s*+键[^\(]+.(.+)\)[^\(]+
    
    .#如果我删除首字母^或使用多行,看起来速度会非常慢。这一个很好,我忘记了+?可能会使它变慢,谢谢!
    go doc regexp/syntax
    
    ^DETAIL:.+?\((.+)\).+?\((.+)\) already exists.
    
    ^DETAIL:\s*+Key[^\(]++.(.+)\)[^\(]+
    .           # <============= here, use \( instead
    (.[^\)]+). already exists