起初,我只需要匹配整数(例如123),所以我只使用[0-9]+
然后,我需要允许一个可选的小数部分(例如123.456),因此我编辑以前的正则表达式并添加\.?[0-9]*
:
[0-9]+\.?[0-9]*
但是,《精通正则表达式》一书却说,我应该改用(\.[0-9])*
,因为\.?[0-9]*
“即使\.
不匹配,也会错误地允许其他数字匹配”。正确的正则表达式应为:
[0-9]+(\.[0-9]*)?
我对这里的差异有些困惑,您能举个例子清楚地说明这两个正则表达式之间的差异吗?
除了可能的ReDoS外,我想知道是否有一个示例,这两个正则表达式给出了不同的匹配结果。
答案 0 :(得分:1)
基本问题
第一个正则表达式的问题
[0-9]+\.?[0-9]*
因为小数点后缀是可选的,即使后面跟着更多的数字,正则表达式也可以在任意位置拆分整数和小数部分。
假设您尝试匹配1234,则正则表达式将被允许将整数部分视为1、12、123或1234中的任何一个,其余数字为小数部分。如果您没有捕获和使用结果,那么看起来似乎是一样的,但是效率很低,因为它以多种不同的方式进行匹配。
但是,贪婪匹配会在有匹配项时为您保存,它将被[0-9]+
捕获。但是请继续阅读...
捕获群体的歧义
例如,考虑使用捕获组保存数字中的整数和小数部分:
([0-9]+)(\.?[0-9]*)
一旦您匹配了数字$1
和$2
(如果您使用的是Perl;语法因语言而异)将分别匹配整数和十进制部分,但是正则表达式允许太多的自由,因此理论上它可以在任何地方破坏它,例如我的1234
示例。当然,贪婪的匹配仍然可以节省您的时间,并且您仍然可以获得正确的答案,但效率较低。但是请继续阅读...
灾难性的回溯
您可能会遇到真正问题的地方是,如果以后添加更多与字符串不匹配的内容:使用此正则表达式,您可能会遇到灾难性的回溯。
考虑此正则表达式
[0-9]+\.?[0-9]* some text
,并尝试匹配1000个数字的字符串,然后匹配不匹配some text
的其他文本。在文本上失败之前,这将具有成倍地成功匹配数字的多种方式。根据您的语言,您可能会收到一条错误消息,说“灾难性的回溯”,或者堆栈溢出,内存不足错误,或者程序只是挂起,似乎除了消耗大量CPU外什么也不做。
但是,此正则表达式立即失败,无需回溯:
[0-9]+(\.[0-9]*)? some text
如果您搜索“灾难性的回溯”,则会发现许多与之相关的问题示例,更多的示例与您的一种或多种方式相似。这是我刚刚发现的一种典型的情况:Regex Pattern Catastrophic backtracking
答案 1 :(得分:0)
这仍将与100.
之类的数字匹配
[0-9]+(\.[0-9]*)?
因为*
意味着匹配零个或多个时间,所以
正确的正则表达式应为
[0-9]+(?:\.[0-9]+)?
[0-9]+
-匹配数字0到9一次或多次(\.[0-9]+)
-匹配.
后跟一个或多个数字 注意:- 如果不使用对小数部分的反向引用,则应使用非捕获组而不是捕获组。 (?:)
用于使群组无法捕获