验证用户名输入字段时出现问题。
正则表达式:
var mailformat = /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,4})+$/;
var letterNumber = /^[0-9a-zA-Z]*$/;
支票:
if (!letterNumber.test($('#login_username').val())
&& !mailformat.test($('#login_username').val())){
...
}
当我想通过此条目验证时,浏览器(chrome和ie)会挂起:
012345678901234567890123456789@
任何人都知道为什么?
我在一个表达式中尝试使用正则表达式,但得到了相同的结果。 (正则表达式是可以的,因为他们分开工作)
答案 0 :(得分:5)
你偶然发现了catastrophic backtracking!
要修复它,您可以使用:
^\w+([.-]\w+)*@\w+([.-]\w+)*(\.\w{2,4})+$
在声明匹配失败之前,正则表达式引擎必须检查应用正则表达式的所有可能方法。
使用\w+([.-]?\w+)*
,每个数字可以由\w+
或([.-]?\w+)*
匹配,失败时([.-]?\w+)*
的行为与(\w+)*
相同。这创建了一个很多状态来回归。
强制使用[.-]
会消除这种歧义。
开启(灾难性)回溯
正则表达式引擎的工作方式是它在正则表达式中移动,尝试将它的每个部分与字符串相匹配。每次它有一个选择,它会保存状态,以便它可以回溯到它,如果这是错误的路径。
为了说明,请参阅.*b
与abc
的匹配方式。
.*
量词是贪婪的,所以它会尽可能匹配:它首先匹配任何内容,然后添加a
,然后添加b
,然后添加{{ 1}}。每次引擎记住它时,选择来添加另一个角色。然后引擎会尝试匹配c
,但我们会在字符串的末尾进行匹配,以便失败。
没什么大不了的,我们回到最后的选择:现在b
只匹配.*
。最终的ab
仍然与字符串结尾b
不匹配。
我们更进一步:c
匹配.*
,a
最终可以匹配。总体匹配项找到:b
在了解正则表达式与输入不匹配之前,引擎必须尝试所有这些替代方案。如果允许字符与正则表达式的多个部分匹配,则该数字会增加。
在你的情况下,它随着字符串的长度呈指数增长,直到返回任何东西都是永远的:这是灾难性的回溯。
Friedl关于这个主题的精彩插图:http://www.foo.be/docs/tpj/issues/vol1_2/tpj0102-0006.html
答案 1 :(得分:2)
因为这里发生了灾难性的回溯。正则表达式中的大多数术语
`^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,4})+$`
几乎匹配测试字符串的全长
`012345678901234567890123456789@`
然后它必须回溯,因为没有发生完全匹配。
例如。 ^\w+
匹配 012345678901234567890123456789 @
^\w+([\.-]?\w+)*
,最终^\w+([\.-]?\w+)*@
匹配完整的字符串
现在有@
之后的单词即可。正则表达式中^\w+([\.-]?\w+)*@\w+
因此它必须回溯并找到前一部分的合适匹配,即。 ^\w+([\.-]?\w+)*@
再一次,这个回溯过程一直持续到找到匹配为止。