TL;DR
使用[.] 代替\. 和[0-9] 代替\d 以避免在某些语言(如Java)中出现转义问题。
感谢 the nameless one 最初认识到这一点。
匹配较大字符串中的浮点数的一个相对简单的模式是:
[+-]?([0-9]*[.])?[0-9]+
这将匹配:
查看working example
如果您还想匹配123.(没有小数部分的句点),那么您需要一个稍长的表达式:
[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)
请参阅 pkeller's answer 以获取有关此模式的更完整说明
如果您想包含更广泛的数字,包括科学记数法和十六进制和八进制等非十进制数字,请参阅我对How do I identify if a string is a number? 的回答。
如果您想验证输入是一个数字(而不是在输入中查找数字),那么您应该用^ 和$ 将模式括起来,如下所示:
^[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)$
不规则正则表达式
在大多数现代语言、API、框架、库等中实现的“正则表达式”基于formal language theory 中开发的概念。但是,软件工程师添加了许多扩展,使这些实现远远超出了正式定义。因此,尽管大多数正则表达式引擎彼此相似,但实际上并没有标准。因此,很大程度上取决于您使用的语言、API、框架或库。
(顺便说一句,为了减少混淆,许多人开始使用“regex”或“regexp”来描述这些增强的匹配语言。请参阅 RexEgg 的 Is a Regex the Same as a Regular Expression? .com 了解更多信息。)
也就是说,大多数正则表达式引擎(实际上,据我所知,所有引擎)都会接受\.。很可能是转义存在问题。
逃跑的麻烦
某些语言内置了对正则表达式的支持,such as JavaScript。对于那些不这样做的语言,转义可能是个问题。
这是因为您基本上是在一种语言中使用一种语言进行编码。例如,Java 在其字符串中使用\ 作为转义字符,因此如果您想在字符串中放置文字反斜杠字符,则必须对其进行转义:
// creates a single character string: "\"
String x = "\\";
然而,正则表达式也使用\字符进行转义,所以如果你想匹配文字\字符,你必须为正则表达式引擎转义它,然后再次转义它对于 Java:
// Creates a two-character string: "\\"
// When used as a regex pattern, will match a single character: "\"
String regexPattern = "\\\\";
在您的情况下,您可能没有在您正在编程的语言中转义反斜杠字符:
// will most likely result in an "Illegal escape character" error
String wrongPattern = "\.";
// will result in the string "\."
String correctPattern = "\\.";
所有这些转义都会变得非常混乱。如果您使用的语言支持raw strings,那么您应该使用它们来减少反斜杠的数量,但并非所有语言都支持(最值得注意的是:Java)。幸运的是,有一个替代方案在某些时候会起作用:
String correctPattern = "[.]";
对于正则表达式引擎,\. 和 [.] 的含义完全相同。请注意,这并不适用于所有情况,例如换行符 (\\n)、左方括号 (\\[) 和反斜杠 (\\\\ 或 [\\])。
关于匹配数字的注意事项
(提示:比你想象的要难)
匹配一个数字是您认为使用正则表达式很容易的事情之一,但实际上非常棘手。让我们逐个看一下您的方法:
[-+]?
匹配可选的- 或+
[0-9]*
匹配 0 个或多个连续数字
\.?
匹配一个可选的.
[0-9]*
匹配 0 个或多个连续数字
首先,我们可以通过对数字使用character class shorthand 来稍微清理一下这个表达式(请注意,这也容易受到上述转义问题的影响):
[0-9] = \d
我将在下面使用\d,但请记住,它与[0-9] 的含义相同。 (嗯,实际上,在某些引擎中,\d 会匹配所有脚本中的数字,所以它会比[0-9] 匹配更多,但这在你的情况下可能并不重要。)
现在,如果您仔细观察,您会发现模式的每一部分都是可选的。该模式可以匹配一个长度为 0 的字符串;仅由 + 或 - 组成的字符串;或者,仅由 . 组成的字符串。这可能不是您想要的。
要解决此问题,首先使用所需的最少字符串(可能是单个数字)“锚定”您的正则表达式会很有帮助:
\d+
现在我们要添加小数部分,但它并没有达到您认为的效果:
\d+\.?\d* /* This isn't quite correct. */
这仍将匹配 123. 之类的值。更糟糕的是,它有一个tinge of evil。句点是可选的,这意味着您有两个并排重复的类(\d+ 和\d*)。如果以错误的方式使用这实际上可能很危险,从而使您的系统容易受到 DoS 攻击。
为了解决这个问题,我们需要将其视为必需(以分隔重复的字符类)而不是将句点视为可选,而是将整个小数部分设为可选:
\d+(\.\d+)? /* Better. But... */
现在看起来好多了。我们需要在第一个数字序列和第二个数字序列之间有一个句点,但有一个致命缺陷:我们无法匹配 .123,因为现在需要一个前导数字。
这实际上很容易解决。我们不需要将数字的“小数”部分设为可选,而是将其视为一个字符序列:1 个或多个数字可能以 . 为前缀,而. 可能以 0 或多个数字为前缀:
(\d*\.)?\d+
现在我们只需添加符号:
[+-]?(\d*\.)?\d+
当然,这些斜线在 Java 中很烦人,所以我们可以在长格式字符类中替换:
[+-]?([0-9]*[.])?[0-9]+
匹配与验证
这在 cmets 中出现过几次,所以我添加了关于匹配与验证的附录。
匹配的目标是在输入中找到一些内容(“大海捞针”)。 验证的目的是确保输入的格式符合预期。
就其性质而言,正则表达式只能匹配文本。给定一些输入,他们要么会找到一些匹配的文本,要么不会。但是,通过使用锚标签(^ 和$)将表达式“捕捉”到输入的开头和结尾,我们可以确保除非整个输入与表达式匹配,否则找不到匹配项,有效地使用正则表达式来 验证。
上述正则表达式 ([+-]?([0-9]*[.])?[0-9]+) 将匹配目标字符串中的一个或多个数字。所以给定输入:
apple 1.34 pear 7.98 version 1.2.3.4
正则表达式将匹配 1.34、7.98、1.2、.3 和 .4。
要验证给定的输入是一个数字,而只是一个数字,请将表达式“捕捉”到输入的开头和结尾,方法是将其包装在锚标签中:
^[+-]?([0-9]*[.])?[0-9]+$
这只会在整个输入是浮点数时找到匹配项,如果输入包含其他字符则不会找到匹配项。因此,给定输入 1.2,将找到匹配项,但给定 apple 1.2 pear 则找不到匹配项。
请注意,某些正则表达式引擎具有 validate、isMatch 或类似函数,它基本上自动完成我所描述的操作,如果找到匹配项则返回 true,如果不匹配则返回 false被发现。另请记住,某些引擎允许您设置更改^ 和$ 定义的标志,匹配行的开头/结尾而不是整个输入的开头/结尾。这通常不是默认设置,但请注意这些标志。