用于解析国际浮点数的正则表达式

7

我需要一个正则表达式来获取可以是数字的值

111.111,11

111,111.11

111,111

将整数部分和小数部分分开,以便我可以使用正确的语法将其存储到数据库中。

我尝试过 ([0-9]{1,3}[,.]?)+([,.][0-9]{2})?,但没有成功,因为它无法检测出第二部分 :(

结果应该看起来像:

111.111,11 -> $1 = 111111; $2 = 11

只是出于好奇,为什么您会有这样的模式: 11.111,111,它是实际值(111,111.11)的反转? - ennuikiller
为了让它傻瓜化,以便用户无需记住正确的模式。 - LuRsT
这实际上非常聪明,因为世界上有许多国家使用逗号作为小数分隔符。要查看列表,请参见此处:http://en.wikipedia.org/wiki/Decimal_separator#Countries_using_Arabic_numerals_with_decimal_comma - Håkon
111,111 = 111111。所以它没有小数点 :) - LuRsT
你也可以把111,111解释为111.111,所以你需要决定如何处理边界情况。 - Håkon
显示剩余2条评论
5个回答

11

第一个回答:

这个匹配模式是 #,###,##0.00

^[+-]?[0-9]{1,3}(?:\,?[0-9]{3})*(?:\.[0-9]{2})?$

这与 #.###.##0,00 相匹配:

^[+-]?[0-9]{1,3}(?:\.?[0-9]{3})*(?:\,[0-9]{2})?$

将两者合并(有更聪明/更短的方法来编写它,但它可以工作):

(?:^[+-]?[0-9]{1,3}(?:\,?[0-9]{3})*(?:\.[0-9]{2})?$)
|(?:^[+-]?[0-9]{1,3}(?:\.?[0-9]{3})*(?:\,[0-9]{2})?$)
你也可以在最后一个逗号(或句号)处添加一个捕获组,以检查使用了哪个符号。

第二个答案:

正如 Alan M 指出的那样,我的先前解决方案可能无法拒绝像 11,111111.00 这样的值,其中一个逗号缺失,但另一个不缺失。经过一些测试,我得出了以下正则表达式,可以避免这个问题:

^[+-]?[0-9]{1,3}
(?:(?<comma>\,?)[0-9]{3})?
(?:\k<comma>[0-9]{3})*
(?:\.[0-9]{2})?$

这需要一些解释:

  • ^[+-]?[0-9]{1,3} 匹配前面的(1到3位)数字;

  • (?:(?<comma>\,?)[0-9]{3})? 匹配一个可选的逗号后跟更多的3位数字,并在一个名为“comma”的组中捕获逗号(或不存在逗号);

  • (?:\k<comma>[0-9]{3})* 匹配零个或多个之前使用的逗号(如果有)后面跟着3位数字;

  • (?:\.[0-9]{2})?$ 匹配字符串末尾的可选“分”。

当然,这只包括#,###,##0.00的情况(不包括#.###.##0,00),但你可以像上面那样连接正则表达式。


最终答案:

现在,一个完整的解决方案。缩进和换行仅用于可读性。

^[+-]?[0-9]{1,3}
(?:
    (?:\,[0-9]{3})*
    (?:.[0-9]{2})?
|
    (?:\.[0-9]{3})*
    (?:\,[0-9]{2})?
|
    [0-9]*
    (?:[\.\,][0-9]{2})?
)$

这个变量捕获了所使用的分隔符:

^[+-]?[0-9]{1,3}
(?:
    (?:(?<thousand>\,)[0-9]{3})*
    (?:(?<decimal>\.)[0-9]{2})?
|
    (?:(?<thousand>\.)[0-9]{3})*
    (?:(?<decimal>\,)[0-9]{2})?
|
    [0-9]*
    (?:(?<decimal>[\.\,])[0-9]{2})?
)$
编辑1: “cents”现在是可选的; 编辑2: 添加了文本; 编辑3: 添加了第二个解决方案; 编辑4: 添加了完整解决方案; 编辑5: 添加标题; 编辑6: 添加捕获; 编辑7: 最后一个答案分成两个版本;

刚刚注意到,千位分隔符不应该是可选的;例如, (?:\.?[0-9]{3})* 应该是 (?:\.[0-9]{3})*。否则,你可能会匹配到像 11,111111.00 或者 1111.111,00 这样的东西。 - Alan Moore
好的,但是如果你想让它们变成可选的呢? - jpbochi
现在它是可选的,并且没有你指出的问题。 :) - jpbochi
哇,正则表达式很棒,但是我不能用它(我正在使用php 5.3),你能为那个版本制作一份吗?即使我必须搜索结果以正确找到组 :) - LuRsT
“千位”组仅会捕获分隔符(,'.')字符。后来我意识到您想要捕获数字本身。我不确定使用原始正则表达式是否可能。您可以使用我编写的正则表达式验证字符串并捕获分隔符。然后,在第二步中,您可以拆分数字并删除分隔符。 - jpbochi
显示剩余8条评论

3

首先使用该正则表达式来确定逗号或点号作为逗号分隔符的使用情况(它获取最后两个中的一个):

[0-9,\.]*([,\.])[0-9]*

我将剥离所有其他与之前不匹配的符号。如果没有匹配项,则已经有一个整数,可以跳过下一步。所选符号的删除可以很容易地使用正则表达式完成,但也有许多其他函数可以更快/更好地完成此操作。

然后您将得到一个以整数形式表示的数字,可能后跟逗号或点和小数部分,其中整数部分和小数部分可以轻松地用以下正则表达式分开。

([0-9]+)[,\.]?([0-9]*)

祝你好运!

编辑:

这里有一个用Python编写的示例,我认为代码应该是自解释的,如果不是,请随时询问。

import re

input = str(raw_input())
delimiterRegex = re.compile('[0-9,\.]*([,\.])[0-9]*')
splitRegex = re.compile('([0-9]+)[,\.]?([0-9]*)')

delimiter = re.findall(delimiterRegex, input)

if (delimiter[0] == ','):
    input = re.sub('[\.]*','', input)
elif (delimiter[0] == '.'):
    input = re.sub('[,]*','', input)

print input

使用此代码,以下输入将产生以下结果:
  • 111.111,11

    111111.11

  • 111,111.11

    111111.11

  • 111,111

    111,111

经过这一步,现在可以轻松修改字符串以满足您的需求。

我很确定这个答案是错误的,但我不能确定,因为你没有说你如何使用正则表达式(但这已经足够让我给你一个负评了)。你能解释一下你如何区分千位分隔符和小数点分隔符吗(并提供测试用例)? - Alan Moore
第一个正则表达式将通过查找最后出现的分隔符来确定十进制分隔符。然后,您可以去掉其他运算符的数字。这样,您就会得到一个没有千位分隔符的数字。其余部分应该很容易。稍后将发布示例代码。 - Håkon
根据 OP 的说法,111,111 中的逗号是千位分隔符(TS)。如果有小数分隔符(DS),则必须后跟正好两个数字(他在问题下的评论中澄清了这一点)。因此,您的第一个正则表达式必须以 ([,.][0-9]{2})? 结束,就像 OP 的那样。但他还试图验证 TS 是否正确分布。 - Alan Moore

1

怎么样

/(\d{1,3}(?:,\d{3})*)(\.\d{2})?/

如果您关心逗号在每3位数字之间的准确分隔验证,或者

/(\d[\d,]*)(\.\d{2})?/

如果你不这样做。


这不会验证他的第一个例子; 111.111,11 - Håkon
真的。我没有注意到那个。抱歉。 - Avi

0
如果我正确理解了您的问题,即您认为结果应该看起来像您所说的“would”一样,那么我认为您只需要将字符类中的逗号去掉,因为它用作分隔符而不是要匹配的内容的一部分。
首先去掉“.”,然后再匹配两个部分。
$value = "111,111.11";
$value =~ s/\.//g;
$value =~ m/(\d+)(?:,(\d+))?/;

$1 = 带有去除小数点的前导整数 $2 = 如果存在,则为undef,否则为逗号后面的数字。


0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接