2013-03-23 21 views
5

Mam prosty EditText, który pozwala użytkownikowi wprowadzić numer, taki jak 45.60 (przykład dla amerykańskiego dolara). I następnie sformatować ten numer przy użyciu następującej metody:Funkcja NumberFormat.parse() kończy działanie dla niektórych łańcuchów walutowych

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

I na moim telefonie z systemem Android, język jest ustawiony na angielski (Stany Zjednoczone) - stąd Locale.getDefault() powinien zwrócić locale nas (i to robi).

Teraz tekst edycji jest poprawnie aktualizowany do: $45.60 (dlatego formatowanie wprowadzonej liczby działa).

Jednakże jeśli próbuję analizować powyższy ciąg "$45.60" stosując następujące metody:

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

nie jest on z:

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

Jeżeli ustawić swój telefon na angielski/UK, formatowanie to "45.60" do "£45.60" działa poprawnie (jak w USA), jednak parsowanie "£45.60" kończy się niepowodzeniem, podobnie jak w przypadku powyższej próbki USA.

Jeśli jednak ustawię mój telefon na niemiecki (Niemcy), formatowanie "45,60" na "45,60€" działa poprawnie, I parsowanie "45,60€" działa również poprawnie!

Jedyna różnica między tymi trzema walutami: euro jest doliczane do kwoty, a dolara i funta są dodawane do kwoty.

Czy ktoś ma pomysł, dlaczego ten sam kod działa dla euro, ale nie dla funta i dolara? Czy czegoś brakuje?

Stworzyłem również testów jednostkowych, aby odtworzyć problem:

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 zasadzie otacza kod napisałem do parsowania ciągów walut, z wyjątkiem, że zajmuje daną locale zamiast domyślnego ustawienia regionalne. W powyższym przykładzie test kończy się niepowodzeniem dla ustawień regionalnych NIEMCY i FRANCJA, ale kończy się niepowodzeniem w przypadku ustawień regionalnych w USA i Wielkiej Brytanii.

Odpowiedz

2

Ponieważ odpowiedzi, które zostały zaproponowane do tej pory, nie całkowicie rozwiązać problem, wziąłem boleśnie amatorskiego podejścia:

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

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

Tak teraz po prostu usuwam wartość String z jej symbolu waluty ... W ten sposób mogę przetworzyć wszystko, co chcę, na przykład: 45,78 lub 45,78 lub 45,78 lub 45,78 € ....

Bez względu na dane wejściowe, symbol waluty jest po prostu rozebrany i kończę na zwykłym numerze. Moje unittests (zobacz OP) zakończyło się pomyślnie.

Jeśli ktoś wymyśli coś lepszego, proszę dać mi znać.

+0

Nie nazwałbym tego "boleśnie amatorskim". Jest to typowy przypadek użycia, szczególnie podczas analizowania danych wejściowych EditText. Zastanawiałem się również nad wymuszaniem poprawnego formatu (w/znak waluty) bezpośrednio w EditText (przy użyciu TextWatchera), ale w końcu używam tego rozwiązania jako mniej uciążliwego dla użytkownika. Dzięki. – pkk

1

Spróbuj NumberFormat.getCurrencyInstance(). Pars() zamiast NumberFormat.getInstance() .parse().

+0

Ok próbował go i to naprawdę nie działa. Muszę być w stanie przeanalizować 44,65 USD oraz 45,65 z tym samym kodem. Jeśli zmienię swój kod zgodnie z sugestią, parsowanie ciągów liczbowych (bez symbolu waluty) już nie działa. I: po tych zmianach mogę przetworzyć 45.56 dolarów, ale 45,56 € nie działa już o ... – AgentKnopf

+1

Jest to ograniczenie procedur parsujących Java. Dla twoich szczególnych wymagań powinieneś spróbować obu. –

+1

Dzięki za wyjaśnienia. Jednak w tym przypadku myślę, że lepiej mi będzie z prostym zastąpieniem symbolu waluty :) - ponieważ jest to jedna rzecz, która sprawdza się w każdym przypadku. I uważam, że jest to trochę kłopotliwe, jeśli potrzebuję dwóch różnych podejść do zrobienia tego samego (przeanalizuj ciąg waluty), tylko dlatego, że niektóre waluty mają poprzedni symbol waluty, podczas gdy inne mają to dołączone: /. Logicznie powinno to być proste: jedna rutyna dla każdego rodzaju analizy waluty. Ponieważ jest to oparte na locale, nie powinno to stanowić problemu, ale niestety nie jest to takie proste. – AgentKnopf

2

Spróbuj następujący:

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

¤ - znak waluty spodziewa mecze z symbolem waluty przez Locale.

inne symbole wzór można zobaczyć przez poniższy link http://docs.oracle.com/javase/6/docs/api/java/text/DecimalFormat.html

+1

Dzięki za sugestię, działa on dla Dolara i funtów, ale nie działa dla euro. Podejrzewam, że w przypadku euro znak waluty jest dołączany, a nie przed? Raczej szukam czegoś, co działa powszechnie. Jedyne, co mogę wymyślić, to usunąć symbol waluty za pomocą wyrażenia regularnego i przeanalizować wartość:/ale wolę tego uniknąć ... – AgentKnopf

+0

jeszcze jedno podejście: Wartość ciągu = "123.5678 €" .replace (nowy DecimalFormatSymbols (Locale.GERMANY) .getCurrencySymbol(), ""); BigDecimal bigDecimal = new BigDecimal (wartość); to nie interesuje Cię aktualny symbol waluty – mokshino

+0

Hej, dziękuję za tę propozycję, to prawie to, co napisałem wczoraj;) (zobacz moją własną odpowiedź poniżej) i tak to teraz robię. – AgentKnopf

0

Musisz znać ustawienia narodowe ciągu, który chcesz przeanalizować, aby mieć parser uwzględniający ustawienia regionalne. Ciąg GBP analizuje numerycznie TYLKO, gdy wartością narodową NumberFormat jest en_GB; nie ma czegoś takiego jak "uniwersalny" parser.

Na przykład, jak działa ciąg "12.000"? Dla nas odpowiedź brzmi: dwanaście; dla de-de odpowiedź wynosi dwanaście tysięcy.

Zawsze używaj funkcji NumberFormat.getCurrencyInstance (java.util.Locale) do analizowania kwot walut.

0

Używam poniżej adaptacją 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)"); 
} 

Jeśli potrzebujesz, aby zapewnić prawidłową Locale jest używany za wygląd użytkownika na Change locale on login

Powiązane problemy