java NumberFormat.parse() 对于某些货币字符串失败

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/15586099/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 20:09:54  来源:igfitidea点击:

NumberFormat.parse() fails for some currency strings

javaandroidcurrencynumberformatexception

提问by AgentKnopf

I have a simple EditText, which allows the user to enter a number such as 45.60(example for American Dollar). I then format this number using the following method:

我有一个简单的EditText,它允许用户输入一个数字,例如45.60(例如美元)。然后我使用以下方法格式化这个数字:

public String format() {
    NumberFormat formatter = NumberFormat.getCurrencyInstance(Locale.getDefault());
    return formatter.format(amount.doubleValue());
}

And on my Android phone, the language is set to English (United States) - hence the Locale.getDefault()should return the US locale (and it does).

在我的 Android 手机上,语言设置为英语(美国) - 因此Locale.getDefault()应该返回美国语言环境(确实如此)。

Now the edit text is correctly updated to: $45.60(hence formatting the entered number works).

现在编辑文本正确更新为:($45.60因此格式化输入的数字有效)。

However if I attempt to parse the above String "$45.60"using the following method:

但是,如果我尝试"$45.60"使用以下方法解析上述字符串:

NumberFormat numberFormat = NumberFormat.getInstance(Locale.getDefault());
Number result = numberFormat.parse(".60");

It fails with:

它失败了:

java.lang.IllegalArgumentException: Failed to parse amount .60 using locale en_US.

If I set my phone to English/ UK, formatting this "45.60"to "£45.60"works correctly (as for US), however parsing "£45.60"fails, just as it does for the above US sample.

如果我把我的电话英语/ UK,格式化这个"45.60""£45.60"工作正常(如美国),但是解析"£45.60"失败,只是因为它为上述美国样本。

However, if I set my phone to German (Germany), formatting "45,60"to "45,60"works correctly, AND parsing "45,60"works correctly as well!

但是,如果我把我的电话给德国(德国),格式化"45,60""45,60"正常运行,并且解析"45,60"工作正常的!

The only difference I see between those three currencies: The Euro is appended to the amount, while the Dollar and the Pound are prepended to the amount.

我看到这三种货币之间的唯一区别:欧元附加到金额,而美元和英镑附加到金额。

Does anyone have an idea, why the same code works for Euro, but not for Pound and Dollar? Am I missing something?

有谁知道为什么相同的代码适用于欧元,但不适用于英镑和美元?我错过了什么吗?

I also created a unit test, to reproduce the issue:

我还创建了一个单元测试,以重现该问题:

public void testCreateStringBased() throws Exception {

    // For German locale
    CurrencyAmount amount = new CurrencyAmount("25,46", Locale.GERMANY);
    assertEquals(25.46, amount.getAsDouble());

    // For French locale
    amount = new CurrencyAmount("25,46", Locale.FRANCE);
    assertEquals(25.46, amount.getAsDouble());

    // For US locale
    amount = new CurrencyAmount(".46", Locale.US);
    assertEquals(25.46, amount.getAsDouble());

    // For UK locale
    amount = new CurrencyAmount("£25.46", Locale.UK);
    assertEquals(25.46, amount.getAsDouble());
}

CurrencyAmountbasically wraps the code I posted for parsing currency strings, except that it takes the given locale instead of the default locale. In the above example, the test succeeds for the GERMANY and FRANCE locale but fails for US and UK locale.

CurrencyAmount基本上包装了我发布的用于解析货币字符串的代码,除了它采用给定的语言环境而不是默认语言环境。在上面的示例中,针对 GERMANY 和 FRANCE 语言环境的测试成功,但针对美国和英国语言环境的测试失败。

回答by AgentKnopf

Since the answers that have been suggested thus far, did not completely solve the problem, I took a painfully amateurish approach:

由于到目前为止提出的答案并没有完全解决问题,我采取了一种痛苦的业余方法:

String value = ",76" 
value = value.replace(getCurrencySymbol(locale), StringUtils.EMPTY);

NumberFormat numberFormat = NumberFormat.getInstance(locale);
Number result = numberFormat.parse(value);

So now I simply strip the String value off it's currency symbol... This way I can process everything I want, such as: 45.78 or 45,78 or $45.78 or 45,78 ....

所以现在我只是从它的货币符号中去除字符串值......这样我可以处理我想要的一切,例如:45.78 或 45,78 或 $45.78 或 45,78 ....

Whatever the input, the currency symbol is simply stripped and I end up with the plain number. My unittests (see OP) now complete successfully.

无论输入什么,货币符号都会被简单地剥离,最后得到纯数字。我的单元测试(见 OP)现在成功完成。

If anyone comes up with something better, please let me know.

如果有人想出更好的东西,请告诉我。

回答by mokshino

Try following:

尝试以下操作:

NumberFormat numberFormat = new DecimalFormat("¤#.00", new DecimalFormatSymbols(Locale.UK));
numberFormat.parse("£123.5678");

¤ - currency sign, expects matches with currency symbol by Locale.

¤ - 货币符号,期望与 Locale 的货币符号匹配。

other pattern symbols you can see by following link http://docs.oracle.com/javase/6/docs/api/java/text/DecimalFormat.html

您可以通过以下链接看到其他模式符号http://docs.oracle.com/javase/6/docs/api/java/text/DecimalFormat.html

回答by Philip Sheard

Try NumberFormat.getCurrencyInstance().parse() instead of NumberFormat.getInstance().parse().

尝试 NumberFormat.getCurrencyInstance().parse() 而不是 NumberFormat.getInstance().parse()。

回答by Delfino

You must know the locale of the string you wish to parse in order to have a locale-aware parser. The GBP string parse to a numeric ONLY when the NumberFormat's locale is en_GB; there is no such thing as a "universal" parser.

您必须知道要解析的字符串的语言环境,才能使用语言环境感知解析器。仅当 NumberFormat 的语言环境为 en_GB 时,GBP 字符串才会解析为数字;没有“通用”解析器这样的东西。

For example, how does the string "12.000" parse? For en-us, the answer is twelve; for de-de, the answer is twelve-thousand.

例如,字符串“12.000”如何解析?对于 en-us,答案是十二;对于德德,答案是一万二千。

Always use NumberFormat.getCurrencyInstance( java.util.Locale ) to parse currency amounts.

始终使用 NumberFormat.getCurrencyInstance( java.util.Locale ) 来解析货币金额。

回答by melutovich

I'm using below adapted from https://dzone.com/articles/currency-format-validation-and

我正在使用以下改编自https://dzone.com/articles/currency-format-validation-and

import java.math.BigDecimal;
import org.apache.commons.validator.routines.*;

BigDecimalValidator currencyValidator = CurrencyValidator.getInstance();
BigDecimal parsedCurrency = currencyValidator.validate(currencyString);
if ( parsedCurrency == null ) {
        throw new Exception("Invalid currency format (please also ensure it is UTF-8)");
}

If you need to insure the correct Locale is being used per user look at Change locale on login

如果您需要确保每个用户使用正确的区域设置,请查看登录更改区域设置

回答by sixro

Sorry, but any answer provided are misleading. This is what I would call a BUG in Java. An example like this explains it better. If I want to print a value in EURusing Locale.USand then I parse it again, it fails unless I specify on the DecimalFormatthe currency (EUR). Using dollars, it works:

抱歉,但提供的任何答案都具有误导性。这就是我所说的 Java 中的 BUG。像这样的例子更好地解释了它。如果我想在EURusing 中打印一个值Locale.US,然后再次解析它,除非我DecimalFormat在货币 ( EUR)上指定,否则它会失败。使用美元,它的工作原理:

        DecimalFormat df = new DecimalFormat("¤#,##0.00", new DecimalFormatSymbols(Locale.US));
        df.setCurrency(Currency.getInstance("EUR"));
        BigDecimal value = new BigDecimal("1.23");
        String text = df.format(value);
        System.out.println(text);

        DecimalFormat df2 = new DecimalFormat("¤#,##0.00", new DecimalFormatSymbols(Locale.US));
        df2.setParseBigDecimal(true);
        BigDecimal parsed = (BigDecimal) df2.parse(text);

        BigDecimalAsserts.assertBigDecimalEquals("parsed value is the same of the original", value, parsed);