为什么这个JavaScript正则表达式会变慢?

时间:2013-01-06 23:55:00

标签: javascript regex

var email = '[John Smith] <johnsmith@gmail.com>';

var re1 = /.*<+(.*)+>.*/;
var re2 = /.*\[+(.*)+\].*/;

var address = email.replace(re1, "$1");
var name = email.replace(re2, "$1");

我发现第二个正则表达式(获得名称)运行速度超慢。但第一个很好。为什么会这样,有没有更好的方法来获得我需要的字符串?

3 个答案:

答案 0 :(得分:15)

你的正则表达式很慢的原因是因为它们是可怕的写

现在,让我们继续说明为什么他们不好。

你的第一个表达式有一个的不必要的令牌。如引导和尾随.* - 它们没有区别。其次,您已将< 0量化为inf时间。为什么?你想要匹配<<<<<<<<email>吗?还是email>?最后,您已经量化了重复组。这太可怕了,因为

  1. 量化的捕获组将覆盖自己
  2. 由于上述说法,使用捕获组是没有意义的,因此它使用了不必要的资源。
  3. 好吧,那是第一个表达。第二个更糟糕,即使您刚刚<>切换[]。你可能会问为什么?我会告诉你原因。 因为它不匹配。你可能会问为什么这么糟糕?因为它会产生我们称之为灾难性回溯的东西。为什么这样做你可能想知道?我会告诉你原因:

    .*会尝试尽可能匹配。事实上,首先,它将消耗整个字符串。显然失败了,所以它回溯了很多次,直到它可以匹配第一个[。太棒了,现在引擎在文字[的字符串的第一个位置找到了一个匹配(从而使.*没有匹配)。现在下一个标记.*将再次匹配所有内容,因为它的贪婪性质。这不起作用,因此引擎回溯。它将继续尝试这样做,直到它匹配字符串。问题是,它永远不会。因为你的贪婪量词被一个需要一个或多个匹配的量化组包围。

    现在,你如何解决这个问题?好吧,您只需从组后面删除+即可。那会解决它。你的正则表达式仍然很糟糕,但它们不会导致引擎回溯百万次。我们怎样才能进一步提高它?通过使用否定的字符类。

    /\[([^]]+)\] <([^>]+)>/

    在此处查看正则表达式的演示:http://regex101.com/r/wS2jN0

    如果你开始使用regex101.com,你会立即注意到回溯问题:http://regex101.com/r/vB8xB0

答案 1 :(得分:2)

不确定您的性能问题(如果有)但您可以使用单个正则表达式来提取这两个值:

var str = '[John Smith] <johnsmith@gmail.com>',
    re = /\[(.+)\] <(.+)>/,
    name = str.match( re )[1],
    email = str.match( re )[2];

console.log( name, email ); //=> "John Smith johnsmith@gmail.com"

答案 2 :(得分:2)

这是因为使用了许多贪婪的.*,结果事实上字符串的格式为"[..] <..>"

每次使用.*而没有?时,RegExp引擎选择整个字符串的其余部分,然后一次向后移动一个字符,因为RegExp的以下部分失败,测试下一个一部分。

正如您重复.*一样,这意味着您要告诉它从RegExp引擎必须回溯的字符串末尾开始,为每个字符运行指数级更多的测试。然后,贪婪的+标志使情况变得更糟,重复了*重复做的事情。

添加? s并不是最好的解决方法,因为您对字符串了解得更多,并且您并未在其中寻找那么多内容。因此,为了减少&#34; bad&#34; ,请做一些事情,例如只匹配您感兴趣的位

var re1 = /\<([^>]*)>/,
    re2 = /\[([^\]]*)\]/;

var address = email.match(re1)[1],
    uname = email.match(re2)[1]; // to avoid `window.name` conflict