如何在Java中解析货币金额(美国或欧盟)浮动值

在欧洲,小数用’ ‘分隔,我们使用可选’ ‘分开成千上万。 我允许货币值:

  • 美式123,456.78表示法
  • 欧式123.456,78表示法

我使用下一个正则表达式(来自RegexBuddy库)来validation输入。 我允许可选的两位数分数和可选的千位分隔符。

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

我想将货币字符串解析为浮点数。 例如

123,456.78应存储为123456.78
123.456,78应存储为123456.78
123.45应存储为123.45
1.234应存储为1234 12.34应存储为12.34

等等…

在Java中有一种简单的方法吗?

 public float currencyToFloat(String currency) { // transform and return as float } 

使用BigDecimal而不是Float


感谢大家的回答。 我已将我的代码更改为使用BigDecimal而不是float。 我会用浮动来保留这个问题的前一部分,以防止人们犯同样的错误。


下一个代码显示了一个函数,它将美国和欧盟货币转换为BigDecimal(String)构造函数接受的字符串。 这就是说一个没有千分隔符的字符串和一个分数点。

  import java.util.regex.Matcher; import java.util.regex.Pattern; public class TestUSAndEUCurrency { public static void main(String[] args) throws Exception { test("123,456.78","123456.78"); test("123.456,78","123456.78"); test("123.45","123.45"); test("1.234","1234"); test("12","12"); test("12.1","12.1"); test("1.13","1.13"); test("1.1","1.1"); test("1,2","1.2"); test("1","1"); } public static void test(String value, String expected_output) throws Exception { String output = currencyToBigDecimalFormat(value); if(!output.equals(expected_output)) { System.out.println("ERROR expected: " + expected_output + " output " + output); } } public static String currencyToBigDecimalFormat(String currency) throws Exception { if(!doesMatch(currency,"^[+-]?[0-9]{1,3}(?:[0-9]*(?:[.,][0-9]{0,2})?|(?:,[0-9]{3})*(?:\\.[0-9]{0,2})?|(?:\\.[0-9]{3})*(?:,[0-9]{0,2})?)$")) throw new Exception("Currency in wrong format " + currency); // Replace all dots with commas currency = currency.replaceAll("\\.", ","); // If fractions exist, the separator must be a . if(currency.length()>=3) { char[] chars = currency.toCharArray(); if(chars[chars.length-2] == ',') { chars[chars.length-2] = '.'; } else if(chars[chars.length-3] == ',') { chars[chars.length-3] = '.'; } currency = new String(chars); } // Remove all commas return currency.replaceAll(",", ""); } public static boolean doesMatch(String s, String pattern) { try { Pattern patt = Pattern.compile(pattern, Pattern.CASE_INSENSITIVE); Matcher matcher = patt.matcher(s); return matcher.matches(); } catch (RuntimeException e) { return false; } } } 

要回答一个稍微不同的问题: 不要使用浮点类型来表示货币值。 它会咬你 。 使用base-10类型(如BigDecimal )或整数类型(如intlong (表示您的值的量 – 例如,以美元计算的便士)。

您将无法存储精确值 – 例如123.45作为浮点数,并且对该值的数学运算(例如乘以税率)将产生舍入误差。

该页面的示例:

 float a = 8250325.12f; float b = 4321456.31f; float c = a + b; System.out.println(NumberFormat.getCurrencyInstance().format(c)); // prints $12,571,782.00 (wrong) BigDecimal a1 = new BigDecimal("8250325.12"); BigDecimal b1 = new BigDecimal("4321456.31"); BigDecimal c1 = a1.add(b1); System.out.println(NumberFormat.getCurrencyInstance().format(c1)); // prints $12,571,781.43 (right) 

在钱方面你不想犯错误。

关于最初的问题,我在一段时间内没有触及Java,但我知道我想远离正则表达式做这种工作。 我看到这个推荐; 它可能会帮助你。 未经测试; 警告开发者。

 try { String string = NumberFormat.getCurrencyInstance(Locale.GERMANY) .format(123.45); Number number = NumberFormat.getCurrencyInstance(locale) .parse("$123.45"); // 123.45 if (number instanceof Long) { // Long value } else { // too large for long - may want to handle as error } } catch (ParseException e) { // handle } 

查找具有与您期望看到的规则相匹配的规则的区域设置。 如果找不到,请按顺序使用多个,或创建自己的自定义NumberFormat 。

我还考虑强制用户以单一规范格式输入值。 123.45和123.456对我的口味看起来太相似了,按照你的规则会导致价值相差1000倍。 这就是数百万的损失 。

作为一种通用解决方案,您可以尝试

 char[] chars = currency.toCharArray(); chars[currency.lastIndexOf(',')] = '.'; currency = new String(chars); 

代替

 if(currency.length()>=3) { char[] chars = currency.toCharArray(); if(chars[chars.length-2] == ',') { chars[chars.length-2] = '.'; } else if(chars[chars.length-3] == ',') { chars[chars.length-3] = '.'; } currency = new String(chars); } 

所以小数部分可以是任何长度。

快速肮脏的黑客可能是:

 String input = input.replaceAll("\.,",""); // remove *any* , or . long amount = Long.parseLong(input); BigDecimal bd = BigDecimal.valueOf(amount).movePointLeft(2); //then you could use: bd.floatValue(); //but I would seriously recommended that you don't use floats for monetary amounts. 

请注意,这仅在输入格式为###。00时才有效,即恰好有2位小数。 例如input == "10,022"将打破这个相当天真的代码。

替代方法是使用BigDecimal(String)构造函数,但是您需要将这些欧元样式的数字转换为使用’。’。 作为小数分隔符,除了删除两个千位分隔符。