Il modo migliore per analizzare Doppio con virgola come separatore decimale?


148

Di seguito si ottiene un Exception:

String p="1,234";
Double d=Double.valueOf(p); 
System.out.println(d);

C'è un modo migliore per analizzare "1,234"per ottenere 1.234di p = p.replaceAll(",",".");:?


17
Nella mia esperienza, sostituire All (), come hai suggerito, è il modo migliore per farlo. Non dipende dalle impostazioni internazionali correnti, è semplice e funziona.
Joonas Pulakka,

1
@Marco Altieri: replaceAll(",",".")sostituisce tutte le virgole con punti. Se non ci sono virgole, non fa nulla. Double.valueOf()funziona (solo) con stringhe che usano il punto come separatore decimale. Nulla qui è influenzato dalle impostazioni internazionali predefinite correnti. docs.oracle.com/javase/8/docs/api/java/lang/…
Joonas Pulakka,

4
L'unico problema replaceAll(",",".")è che funzionerà solo se è presente una sola virgola: ovvero: verrà lanciato 1.234.567 java.lang.NumberFormatException: multiple points. Una regex con lookahead positivo sarà sufficiente p.replaceAll(",(?=[0-9]+,)", "").replaceAll(",", ".")Altro su: regular-expressions.info/lookaround.html
artemisian

2
Non c'è problema. NumberFormatException è buona. Come fai a sapere quale virgola è quella giusta? Il formato è errato e tutto ciò che puoi fare è mostrare un messaggio leggibile meglio dell'eccezione per l'utente.
L'incredibile

2
@TheincredibleJan No, il formato non è sbagliato. Alcune impostazioni locali usano la virgola come separatore delle migliaia, quindi puoi avere più di una di esse in un numero e tecnicamente è ancora un input valido.
Vratislav Jindra,

Risposte:


206

Usa java.text.NumberFormat :

NumberFormat format = NumberFormat.getInstance(Locale.FRANCE);
Number number = format.parse("1,234");
double d = number.doubleValue();

11
Funziona solo se l'attuale locale predefinita utilizza una virgola come separatore decimale.
Joonas Pulakka,

6
Per confondere ulteriormente le cose, alcune versioni locali usano la virgola come separatore delle migliaia , nel qual caso "1.234" analizzerebbe la 1234.0 invece di generare un errore.
Joonas Pulakka,

17
Il problema con NumberFormat è che ignorerà silenziosamente i caratteri non validi. Quindi se provi ad analizzare "1,23abc" restituirà felicemente 1,23 senza indicarti che la stringa passata conteneva caratteri non analizzabili. In alcune situazioni che potrebbero essere effettivamente desiderabili, ma non penso che sia solitamente il comportamento desiderato.
E-Riz,

7
per la TURCHIA, dovresti usare NumberFormat.getInstance (new Locale (tr_TR))
Günay Gültekin,

2
per chi usa ciò che il separatore vede en.wikipedia.org/w/…
fiffy

67

Puoi usarlo (la locale francese ha ,come separatore decimale)

NumberFormat nf = NumberFormat.getInstance(Locale.FRANCE);
nf.parse(p);

Oppure puoi usare java.text.DecimalFormate impostare i simboli appropriati:

DecimalFormat df = new DecimalFormat();
DecimalFormatSymbols symbols = new DecimalFormatSymbols();
symbols.setDecimalSeparator(',');
symbols.setGroupingSeparator(' ');
df.setDecimalFormatSymbols(symbols);
df.parse(p);

Sì ... se non impostiamo il separatore dei mille raggruppamenti e utilizziamo solo il formato francese, un numero in formato spagnolo (1.222.222,33) verrà convertito in "1 222 222,33", che non è quello che voglio . Quindi grazie!
WesternGun

1
Un'altra cosa è che le impostazioni locali spagnole non sono elencate come "default" e non posso costruire un Localeformato corretto con new Locale("es", "ES")e quindi analizzare automaticamente la stringa numerica con NumberFormat, ,come separatore decimale e .come separatore dei mille gruppi. DecimalFormatFunziona solo .
WesternGun

Perché non tutti i paesi sono disponibili lì? Mi sento strano nell'usare le impostazioni locali francesi per la formattazione dei numeri polacchi ...
Riga

18

Come sottolinea E-Riz, NumberFormat.parse (String) analizza "1,23abc" come 1,23. Per prendere l'intero input possiamo usare:

public double parseDecimal(String input) throws ParseException{
  NumberFormat numberFormat = NumberFormat.getNumberInstance(Locale.getDefault());
  ParsePosition parsePosition = new ParsePosition(0);
  Number number = numberFormat.parse(input, parsePosition);

  if(parsePosition.getIndex() != input.length()){
    throw new ParseException("Invalid input", parsePosition.getIndex());
  }

  return number.doubleValue();
}

2
Questa strategia è spiegata in dettaglio qui: ibm.com/developerworks/library/j-numberformat
Janus Varmarken

7
Double.parseDouble(p.replace(',','.'))

... è molto veloce in quanto cerca l'array di caratteri sottostante su base char-by-char. Le versioni di sostituzione stringa compilano un RegEx da valutare.

Sostanzialmente sostituire (char, char) è circa 10 volte più veloce e dato che farai questo tipo di cose nel codice di basso livello ha senso pensarci. L'ottimizzatore Hot Spot non lo capirà ... Certamente no sul mio sistema.


4

Se non conosci le impostazioni internazionali corrette e la stringa può avere un migliaio di separatori, questa potrebbe essere l'ultima risorsa:

    doubleStrIn = doubleStrIn.replaceAll("[^\\d,\\.]++", "");
    if (doubleStrIn.matches(".+\\.\\d+,\\d+$"))
        return Double.parseDouble(doubleStrIn.replaceAll("\\.", "").replaceAll(",", "."));
    if (doubleStrIn.matches(".+,\\d+\\.\\d+$"))
        return Double.parseDouble(doubleStrIn.replaceAll(",", ""));
    return Double.parseDouble(doubleStrIn.replaceAll(",", "."));

Attenzione: questo analizzerà felicemente stringhe come "R 1 52.43,2" a "15243.2".


4

Questo è il metodo statico che uso nel mio codice:

public static double sGetDecimalStringAnyLocaleAsDouble (String value) {

    if (value == null) {
        Log.e("CORE", "Null value!");
        return 0.0;
    }

    Locale theLocale = Locale.getDefault();
    NumberFormat numberFormat = DecimalFormat.getInstance(theLocale);
    Number theNumber;
    try {
        theNumber = numberFormat.parse(value);
        return theNumber.doubleValue();
    } catch (ParseException e) {
        // The string value might be either 99.99 or 99,99, depending on Locale.
        // We can deal with this safely, by forcing to be a point for the decimal separator, and then using Double.valueOf ...
        //http://stackoverflow.com/questions/4323599/best-way-to-parsedouble-with-comma-as-decimal-separator
        String valueWithDot = value.replaceAll(",",".");

        try {
          return Double.valueOf(valueWithDot);
        } catch (NumberFormatException e2)  {
            // This happens if we're trying (say) to parse a string that isn't a number, as though it were a number!
            // If this happens, it should only be due to application logic problems.
            // In this case, the safest thing to do is return 0, having first fired-off a log warning.
            Log.w("CORE", "Warning: Value is not a number" + value);
            return 0.0;
        }
    }
}

5
Che cosa succede se le impostazioni internazionali predefinite sono simili al tedesco, dove una virgola indica una posizione decimale? Potresti passare, ad esempio "1.000.000" che non analizzerebbe le impostazioni locali tedesche e verrebbe quindi sostituito da "1.000.000" che non è un doppio valido.
Eddie Curtis,

Ciao @jimmycar, ho appena aggiornato la mia risposta per utilizzare la versione corrente del mio metodo statico. Spero che questo risolva il tuo problema! Pete
Pete,


0

Nel caso in cui non si conosca la locale del valore della stringa ricevuta e non sia necessariamente la stessa locale della locale predefinita corrente, è possibile utilizzare questo:

private static double parseDouble(String price){
    String parsedStringDouble;
    if (price.contains(",") && price.contains(".")){
        int indexOfComma = price.indexOf(",");
        int indexOfDot = price.indexOf(".");
        String beforeDigitSeparator;
        String afterDigitSeparator;
        if (indexOfComma < indexOfDot){
            String[] splittedNumber = price.split("\\.");
            beforeDigitSeparator = splittedNumber[0];
            afterDigitSeparator = splittedNumber[1];
        }
        else {
            String[] splittedNumber = price.split(",");
            beforeDigitSeparator = splittedNumber[0];
            afterDigitSeparator = splittedNumber[1];
        }
        beforeDigitSeparator = beforeDigitSeparator.replace(",", "").replace(".", "");
        parsedStringDouble = beforeDigitSeparator+"."+afterDigitSeparator;
    }
    else {
        parsedStringDouble = price.replace(",", "");
    }

    return Double.parseDouble(parsedStringDouble);

}

Restituirà un doppio, qualunque sia la locale della stringa. E non importa quante virgole o punti ci siano. Quindi il passaggio 1,000,000.54funzionerà, 1.000.000,54quindi non dovrai più fare affidamento sulla locale predefinita per analizzare la stringa. Il codice non è ottimizzato come può essere, quindi qualsiasi suggerimento è il benvenuto. Ho provato a testare la maggior parte dei casi per assicurarmi che risolva il problema, ma non sono sicuro che copra tutto. Se trovi un valore di rottura fammelo sapere.


-5

Questo farebbe il lavoro:

Double.parseDouble(p.replace(',','.')); 

6
La domanda iniziale diceva "Esiste un modo migliore per analizzare" 1.234 "per ottenere 1.234 di: p = p.replaceAll (", ",". ");" , se ritieni che replacedifferisca in modo significativo dall'uso replaceAll, spiega perché.
SuperBiasedMan
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.