2013-03-23 17 views
5

Ho un semplice EditText, che consente all'utente di inserire un numero come 45.60 (esempio per dollaro americano). poi ho formattare questo numero utilizzando il seguente metodo:NumberFormat.parse() non riesce per alcune stringhe di valuta

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

E sul mio telefono Android, la lingua è impostata su Inglese (Stati Uniti) - da qui il Locale.getDefault() dovrebbe restituire il locale degli Stati Uniti (e lo fa).

Ora il testo di modifica viene aggiornato correttamente a: $45.60 (quindi la formattazione del numero inserito funziona).

Tuttavia, se si tenta di analizzare la stringa sopra "$45.60" utilizzando il seguente metodo:

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

non riesce con:

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

Se ho impostato il mio telefono a Inglese/Regno Unito, la formattazione di questa "45.60" a "£45.60" funziona correttamente (come per gli Stati Uniti), tuttavia l'analisi "£45.60" non riesce, proprio come avviene per il campione statunitense sopra riportato.

Tuttavia, se si imposta il telefono in tedesco (Germania), la formattazione "45,60" in "45,60€" funziona correttamente e l'analisi "45,60€" funziona correttamente!

L'unica differenza che vedo tra queste tre valute: l'euro viene aggiunto all'importo, mentre il dollaro e la sterlina sono preposti all'importo.

Qualcuno ha un'idea, perché lo stesso codice funziona per euro, ma non per sterlina e dollaro? Mi sto perdendo qualcosa?

Ho anche creato un test di unità, di riprodurre il problema:

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 avvolge in pratica il codice che ho postato per analizzare le stringhe di valute, la differenza che accetta il locale data invece della localizzazione predefinita. Nell'esempio precedente, il test ha esito positivo per le impostazioni internazionali di GERMANIA e FRANCIA, ma non riesce per le impostazioni internazionali per Stati Uniti e Regno Unito.

risposta

2

Dal momento che le risposte che sono state proposte finora, non ha risolto completamente il problema, ho preso un approccio dolorosamente dilettantesco:

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

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

Così ora spoglio semplicemente il valore di stringa fuori dal suo simbolo di valuta ... In questo modo posso elaborare tutto ciò che voglio, ad esempio: 45.78 o 45,78 o $ 45,78 o 45,78 € ....

Indipendentemente dall'input, il simbolo di valuta viene semplicemente rimosso e si ottiene il numero normale. Le mie unittests (vedi OP) ora sono completate con successo.

Se qualcuno esce con qualcosa di meglio, per favore fatemelo sapere.

+0

Non lo definirei "dolorosamente amatoriale". È un caso d'uso comune, specialmente quando si analizza l'input di EditText. Ho anche considerato di imporre il formato corretto (w/currency sign) direttamente su EditText (usando TextWatcher), ma alla fine uso questa soluzione come meno intrusiva per l'utente. Grazie. – pkk

1

Prova NumberFormat.getCurrencyInstance(). Parse() anziché NumberFormat.getInstance(). Parse().

+0

Ok provato e non funziona davvero. Devo essere in grado di analizzare $ 45,65 e 45,65 con lo stesso codice. Se cambio il mio codice come suggerito, l'analisi di stringhe di numeri (senza simbolo di valuta) non funziona più. E: dopo queste modifiche posso analizzare $ 45,56 ma 45,56 € non funziona più oO ... – AgentKnopf

+1

Questa è una limitazione delle routine di analisi di Java. Per le tue esigenze particolari, dovresti provare entrambi. –

+1

Grazie per questo chiarimento. Comunque, in quel caso, penso che sto meglio con la mia semplice sostituzione del simbolo di valuta :) - perché questa è una cosa, che funziona per tutti i casi. E lo trovo un po 'macchinoso, se ho bisogno di due approcci diversi per fare la stessa cosa (analizzare una stringa di valuta), solo perché alcune valute hanno un simbolo di valuta preposto, mentre altre lo hanno aggiunto: /. Logicamente dovrebbe essere semplice: una routine per ogni tipo di analisi valutaria. Dal momento che è basato sulle impostazioni internazionali, non dovrebbe esserci alcun problema, ma purtroppo non è così semplice. – AgentKnopf

2

Prova seguente:

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

¤ - segno di valuta, si aspetta che le partite con il simbolo di valuta per Locale.

altri simboli modello si può vedere dal seguente link http://docs.oracle.com/javase/6/docs/api/java/text/DecimalFormat.html

+1

Grazie per il suggerimento, funziona per Dollar and Pounds, ma non funziona per Euro. Sospetto, perché nel caso dell'euro il segno di valuta viene aggiunto, non anteposto? Sto piuttosto cercando qualcosa che funzioni universalmente. L'unica altra cosa che posso inventare è rimuovere il simbolo della valuta usando regex e analizzare il valore:/ma preferirei evitare che ... – AgentKnopf

+0

un altro approccio: Valore stringa = "123.5678 €" .replace (new DecimalFormatSymbols (Locale.GERMANY) .getCurrencySymbol(), ""); BigDecimal bigDecimal = new BigDecimal (valore); è di aiuto che non ti interessa il simbolo corrente della valuta – mokshino

+0

Ehi, grazie per quella proposta, è praticamente quello che ho postato ieri;) (vedi la mia risposta qui sotto) ed è così che sto facendo adesso. – AgentKnopf

0

È necessario conoscere la locale della stringa che si desidera analizzare per avere un parser compatibile con la lingua. L'analisi della stringa GBP su SOLO numerico quando le impostazioni internazionali di NumberFormat sono en_GB; non esiste un parser "universale".

Ad esempio, come si analizza la stringa "12.000"? Per noi, la risposta è dodici; per de-de, la risposta è dodicimila.

Utilizzare sempre NumberFormat.getCurrencyInstance (java.util.Locale) per analizzare gli importi in valuta.

0

sto usando qui di seguito adattato da 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)"); 
} 

Se è necessario per assicurare il corretto Locale viene utilizzato per guardare utente alla Change locale on login