简体   繁体   English

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

[英]NumberFormat.parse() fails for some currency strings

I have a simple EditText , which allows the user to enter a number such as 45.60 (example for American Dollar).我有一个简单的EditText ,它允许用户输入一个数字,例如45.60 (例如美元)。 I then format this number using the following method:然后我使用以下方法格式化这个数字:

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("$45.60");

It fails with:它失败了:

java.lang.IllegalArgumentException: Failed to parse amount $45.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.如果我将手机设置为英语/英国,则将此"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("$25.46", Locale.US);
    assertEquals(25.46, amount.getAsDouble());

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

CurrencyAmount basically wraps the code I posted for parsing currency strings, except that it takes the given locale instead of the default locale. CurrencyAmount基本上包装了我发布的用于解析货币字符串的代码,除了它采用给定的语言环境而不是默认语言环境。 In the above example, the test succeeds for the GERMANY and FRANCE locale but fails for US and UK locale.在上面的示例中,针对 GERMANY 和 FRANCE 语言环境的测试成功,但针对美国和英国语言环境的测试失败。

Since the answers that have been suggested thus far, did not completely solve the problem, I took a painfully amateurish approach:由于到目前为止提出的答案并没有完全解决问题,我采取了一种痛苦的业余方法:

String value = "$24,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.如果有人想出更好的东西,请告诉我。

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

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

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;仅当 NumberFormat 的语言环境为 en_GB 时,GBP 字符串才会解析为数字; there is no such thing as a "universal" parser.没有“通用”解析器这样的东西。

For example, how does the string "12.000" parse?例如,字符串“12.000”如何解析? For en-us, the answer is twelve;对于 en-us,答案是十二; for de-de, the answer is twelve-thousand.对于德德,答案是一万二千。

Always use NumberFormat.getCurrencyInstance( java.util.Locale ) to parse currency amounts.始终使用 NumberFormat.getCurrencyInstance( java.util.Locale ) 来解析货币金额。

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如果您需要确保每个用户使用正确的区域设置,请查看登录更改区域设置

Sorry, but any answer provided are misleading.抱歉,但提供的任何答案都具有误导性。 This is what I would call a BUG in Java.这就是我所说的 Java 中的 BUG。 An example like this explains it better.像这样的例子更好地解释了它。 If I want to print a value in EUR using Locale.US and then I parse it again, it fails unless I specify on the DecimalFormat the currency ( EUR ).如果我想使用Locale.USEUR打印一个值,然后我再次解析它,除非我在DecimalFormat上指定货币( EUR ),否则它会失败。 Using dollars, it works:使用美元,它的工作原理:

        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);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM