正则表达式需要很长时间才能进行评估



在登录时,我需要只允许用户名(字母数字和一些特殊字符)或电子邮件地址或用户名\域格式。为此,我使用了带有 or (|) 条件的正则表达式。除此之外,我需要允许一些其他语言字符,如日语、中文等,因此将它们也包含在同一个正则表达式中。现在,问题是当我输入字符 (>=30) 和@或一些特殊字符时,此正则表达式的评估需要几秒钟,浏览器进入挂起模式。

export const usernameRegex = /(^[a-zA-Z0-9._~^#!%+-]+@[a-z0-9.-]+.[a-z]{2,4})+|^[a-zA-Z0-9._~^#!-]+\([._-~^#!]|[p{Ll}p{Lm}p{Lt}a-zA-Z0-9-u3000-u303fu3040-u309fu30a0-u30ffuff00-uff9fu4e00-u9fafu3400-u4dbfu3130-u318FuAC00-uD7AF])+|^([._-~^#!]|[p{Ll}p{Lm}p{Lt}a-zA-Z0-9-u3000-u303fu3040-u309fu30a0-u30ffuff00-uff9fu4e00-u9fafu3400-u4dbfu3130-u318FuAC00-uD7AF])+$/gu;

当我尝试删除其他语言字符集(例如[p{Ll}p{Lm}p{Lt}a-zA-Z0-9-u3000-u303fu3040-u309fu30a0-u30ffuff00-uff9fu4e00-u9fafu3400-u4dbfu3130-u318FuAC00-uD7AF])+|^([._-~^#!]|[p{Ll}p{Lm}p{Lt}a-zA-Z0-9-u3000-u303fu3040-u309fu30a0-u30ffuff00-uff9fu4e00-u9fafu3400-u4dbfu3130-u318FuAC00-uD7AF])时,它工作正常。

我知道通常正则表达式看起来很简单,但它在引擎盖下做了很多事情。是否需要在此正则表达式中进行任何修改,以便无需花时间进行评估。任何帮助都非常感谢!

有效文本:

stackoverflow,
stackoverflow1~,
stackoverflow!#~^-,
stackoverflow@g.co,
stackoverflow!#~^-@g.co,
こんにちは,
你好,
treeguava

编辑:

例如,导致问题的输入 stackoverflowstackoverflowstackoverflow@

在给出上述文本时,需要很长时间。

https://i.stack.imgur.com/Y7MlE.jpg

您的正则表达式似乎由三个与|连接的正则表达式组成

(^[a-zA-Z0-9._~^#!%+-]+@[a-z0-9.-]+.[a-z]{2,4})+
^[a-zA-Z0-9._~^#!-]+\([._-~^#!]|[p{Ll}p{Lm}p{Lt}a-zA-Z0-9-u3000-u303fu3040-u309fu30a0-u30ffuff00-uff9fu4e00-u9fafu3400-u4dbfu3130-u318FuAC00-uD7AF])+
^([._-~^#!]|[p{Ll}p{Lm}p{Lt}a-zA-Z0-9-u3000-u303fu3040-u309fu30a0-u30ffuff00-uff9fu4e00-u9fafu3400-u4dbfu3130-u318FuAC00-uD7AF])+$
  • 首先正则表达式(^...)+您认为从字符串开头开始的整个模式可以发生多少次。要么是第二次出现,要么从字符串的开头开始,不能两者兼而有之。

    所以^[a-zA-Z0-9._~^#!%+-]+@[a-z0-9.-]+.[a-z]{2,4}

  • 第 2 部分和第 3 部分基本
  • 相同,只是第 2 部分包含此块[a-zA-Z0-9._~^#!-]+\后跟第 3 部分的其余部分。

    因此,让我们将它们组合起来:^(?:[a-zA-Z0-9._~^#!-]+\)? ...并确保尽可能使用非捕获组。

  • ([abc]|[def])+可以简化为[abcdef]+。这个顺便说一句。是扼杀你表现的部分。

  • 您的正则表达式以$结尾。这只是最后一部分的一部分,但我想你总是想匹配整个字符串?因此,让我们将所有 3 个(现在是 2 个)部分都^ ... $

总结:

/^[a-zA-Z0-9._~^#!%+-]+@[a-z0-9.-]+.[a-z]{2,4}$|^(?:[a-zA-Z0-9._~^#!-]+\)?[._-~^#!p{Ll}p{Lm}p{Lt}a-zA-Z0-9-u3000-u303fu3040-u309fu30a0-u30ffuff00-uff9fu4e00-u9fafu3400-u4dbfu3130-u318FuAC00-uD7AF]+$/u

一个简单的正则表达式如何尝试匹配字符串的 JS 示例,以及它如何失败、回溯、重试|的另一端等等。

// let's implement what `/([a-z]|[p{Ll}])+/u` would do, 
// how it would try to match something.
const a = /[a-z]/; // left part
const b = /[p{Ll}]/u; // right part
const string = "abc,";
const testNextCharacter = (index) => {
if (index === string.length) {
return true;
}
const pattern = index + "  ".repeat(index + 1) + "%o.test(%o)";
const character = string.charAt(index);
console.log(pattern, a, character);
// checking the left part && if successful checking the next character
if (a.test(character) && testNextCharacter(index + 1)) {
return true;
}
// checking the right part && if successful checking the next character
console.log(pattern, b, character);
if (b.test(character) && testNextCharacter(index + 1)) {
return true;
}
return false;
}
console.log("result", testNextCharacter(0));
.as-console-wrapper{top:0;max-height:100%!important}

而这只有 4 个字符。您为什么不尝试使用 5,6 个字符来了解 20 个字符将有多少工作。

最新更新