如何在Java中解析货币金额(美国或欧盟)以浮动值
在欧洲,小数用“,”分隔,我们使用可选的“”分隔千。我允许使用以下货币值:如何在Java中解析货币金额(美国或欧盟)以浮动值,java,currency,Java,Currency,在欧洲,小数用“,”分隔,我们使用可选的“”分隔千。我允许使用以下货币值: 美式123456.78符号 欧洲风格123.456,78符号 我使用下一个正则表达式(来自RegexBuddy库)来验证输入。我允许可选的两位小数和可选的千位分隔符 ^[+-]?[0-9]{1,3}(?:[0-9]*(?:[.,][0-9]{0,2})?|(?:,[0-9]{3})*(?:\.[0-9]{0,2})?|(?:\.[0-9]{3})*(?:,[0-9]{0,2})?)$ 我想将货币字符串解析为浮动。比
- 美式123456.78符号
- 欧洲风格123.456,78符号
^[+-]?[0-9]{1,3}(?:[0-9]*(?:[.,][0-9]{0,2})?|(?:,[0-9]{3})*(?:\.[0-9]{0,2})?|(?:\.[0-9]{3})*(?:,[0-9]{0,2})?)$
我想将货币字符串解析为浮动。比如说
123456.78应存储为123456.78123.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。我将把这个问题的前一部分保留为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;
}
}
}
回答一个稍微不同的问题:不要使用float类型来表示货币值。改为使用基数为10的类型,如
BigDecimal
,或整数类型,如int
或long
(表示您的价值量-例如,以美元表示的便士)
您将无法将准确的值(比如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了,但我知道我想远离regex来做这类工作。我认为这是建议的;这可能对你有帮助。未经测试;警告开发者
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
}
查找具有与您期望看到的规则相匹配的区域设置。如果找不到一个,请按顺序使用多个,或者创建自己的
<>我也会考虑强制用户以单一的规范格式输入值。123.45和123.456看起来太相似了,不符合我的口味,按照你的规则,它们的值会相差1000倍 一个快速而肮脏的黑客可能是:
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==“10022”
将打破这个相当幼稚的代码
另一种方法是使用
BigDecimal(String)
构造函数,但您需要将这些欧式数字转换为使用“.”作为十进制分隔符,此外还要删除这两个数字的千个分隔符。作为通用解决方案,您可以尝试
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);
}
所以分数部分可以是任意长度。试试这个
Locale slLocale = new Locale("de","DE");
NumberFormat nf5 = NumberFormat.getInstance(slLocale);
if(nf5 instanceof DecimalFormat) {
DecimalFormat df5 = (DecimalFormat)nf5;
try {
DecimalFormatSymbols decimalFormatSymbols = DecimalFormatSymbols.getInstance(slLocale);
decimalFormatSymbols.setGroupingSeparator('.');
decimalFormatSymbols.setDecimalSeparator(',');
df5.setDecimalFormatSymbols(decimalFormatSymbols);
df5.setParseBigDecimal(true);
ParsePosition pPosition = new ParsePosition(0);
BigDecimal n = (BigDecimal)df5.parseObject("3.321.234,56", pPosition);
System.out.println(n);
}catch(Exception exp) {
exp.printStackTrace();
}
}
那么1.23转换成什么呢?你的规则集是矛盾的。为什么不利用应用程序客户端的本地化功能呢?好吧,我的错。。。但事实上,试图在不知道它来自的地区的情况下破译它有一股难闻的味道。嗯,基本上,我最感兴趣的是解决欧盟符号风格的问题。但如果能有一个标准的解决方案就好了。@sergio:更新了我的答案。请尝试现有的特定于区域设置的数字格式,或者创建一个具有预期格式的自定义格式。如果我理解您的意思,请让我看看。你的意思是我应该使用int并将值存储在数据库中,作为美分。12.35作为12*100+35=1235。听起来很聪明,但我仍然需要一个函数来将货币字符串转换为cents。你会的,是的。我上面写的并没有解决这个问题。我看到你建议用花车来处理钱,警钟响了。认真听这个家伙说,用花车来赚钱会在某个时候伤害你。是的,这是正确的。有关进一步的讨论以及如何出错的示例,请参阅有效Java(第二版),第48项,“如果需要精确答案,请避免浮点和双精度”。引用一些要点:“float和double类型特别不适合于货币计算[…]”,“解决这个问题的正确方法是使用BigDecimal、int或long进行货币计算。”如果我没记错的话,Java谜题书也在其中一个谜题中处理了这一问题。>Aternative使用BigDecimal(字符串)构造函数,但您需要将这些欧式数字转换为使用“.”作为十进制分隔符,此外还需要删除这两个数字的千个分隔符。我可以做一个正则表达式来替换,加上最后两个数字。加上最后两位数字,我就可以用除了数字以外的任何东西来替换所有的点和逗号。在最后两位数之前提交。我应该工作,但似乎容易出错。