2010-09-20 14 views
95

Sto provando a leggere alcuni valori BigDecimal dalla stringa. Diciamo che ho questa stringa: "1.000.000.000.999999999999999" e voglio ricavarne un BigDecimal. Qual è il modo di farlo?Conversione da Safe String a BigDecimal

Prima di tutto, non mi piacciono le soluzioni che sostituiscono le stringhe (sostituendo le virgole ecc.). Penso che ci dovrebbe essere un certo programma di formattazione per fare quel lavoro per me.

Ho trovato una classe DecimalFormatter, tuttavia, poiché funziona con doppio, si perdono enormi quantità di precisione.

Quindi, come posso farlo?

+2

Perché no String.replaceAll? – TofuBeer

+0

Dato il formato personalizzato, è difficile convertire il formato in formato compatibile con BigDecimal. – bezmax

+2

* "Dato che il formato personalizzato è un problema ..." * Non so, questo tipo separa i domini del problema. Prima si pulisce la roba leggibile dall'uomo e si passa a qualcosa che sappia come trasformare correttamente il risultato in un 'BigDecimal'. –

risposta

79

Verificare setParseBigDecimal in DecimalFormat. Con questo setter, parse restituirà un BigDecimal per te.

+1

Il costruttore nella classe BigDecimal non supporta i formati personalizzati. L'esempio che ho fornito nella domanda utilizza il formato personalizzato (virgole). Non puoi analizzarlo con BigDecimal usando il suo costruttore. – bezmax

+23

Se stai per ** completamente ** cambiare la tua risposta, ti suggerirei di menzionarla nella risposta. Altrimenti sembra molto strano quando le persone hanno sottolineato perché la tua risposta originale non ha alcun senso. :-) –

+1

Sì, non ho notato quel metodo. Grazie, sembra essere il modo migliore per farlo. Ora provalo e, se funziona correttamente, accetta la risposta. – bezmax

3

Ecco come lo farei:

public String cleanDecimalString(String input, boolean americanFormat) { 
    if (americanFormat) 
     return input.replaceAll(",", ""); 
    else 
     return input.replaceAll(".", ""); 
} 

Ovviamente, se questo dovesse andare nel codice di produzione, non sarebbe stato così semplice.

Non vedo alcun problema con la semplice rimozione delle virgole dalla stringa.

+0

E se la stringa non è in formato americano? In Germania, sarebbe 1.000.000.000,999999999999999 –

+0

@Steve, buona chiamata – jjnguy

+2

Quindi si utilizza il locale per ripulirlo dalle cose corrette. –

45
String value = "1,000,000,000.999999999999999"; 
BigDecimal money = new BigDecimal(value.replaceAll(",", "")); 
System.out.println(money); 

codice completo per dimostrare che nessun NumberFormatException si butta:

import java.math.BigDecimal; 

public class Tester { 
    public static void main(String[] args) { 
     // TODO Auto-generated method stub 
     String value = "1,000,000,000.999999999999999"; 
     BigDecimal money = new BigDecimal(value.replaceAll(",", "")); 
     System.out.println(money); 
    } 
} 

uscita

1000000000,999999999999999

+0

@Steve McLeod .... no, l'ho appena testato .... il mio valore è '1000000000.999999999999999' –

+7

Provare con locale TEDESCO, e 1.000.000.000,999999999999 – TofuBeer

+0

@ # TofuBeer .... l'esempio era 1,000,000,000.999999999999999.Se dovessi fare il tuo locale, dovrei sostituire tutti i punti nello spazio ... –

2
resultString = subjectString.replaceAll("[^.\\d]", ""); 

rimuoverà tutti i caratteri tranne le cifre e il punto dalla stringa.

Per renderlo compatibile con le impostazioni internazionali, è possibile utilizzare getDecimalSeparator() da java.text.DecimalFormatSymbols. Non so di Java, ma potrebbe assomigliare a questo:

sep = getDecimalSeparator() 
resultString = subjectString.replaceAll("[^"+sep+"\\d]", ""); 
+0

Quale darà risultati imprevisti per i numeri non americani - vedi i commenti alla risposta di Justin. –

+0

Lo so, stavo solo modificando la mia risposta quando hai commentato :) –

5

Il codice potrebbe essere più pulito, ma questo sembra fare il trucco per diverse impostazioni internazionali.

import java.math.BigDecimal; 
import java.text.DecimalFormatSymbols; 
import java.util.Locale; 


public class Main 
{ 
    public static void main(String[] args) 
    { 
     final BigDecimal numberA; 
     final BigDecimal numberB; 

     numberA = stringToBigDecimal("1,000,000,000.999999999999999", Locale.CANADA); 
     numberB = stringToBigDecimal("1.000.000.000,999999999999999", Locale.GERMANY); 
     System.out.println(numberA); 
     System.out.println(numberB); 
    } 

    private static BigDecimal stringToBigDecimal(final String formattedString, 
               final Locale locale) 
    { 
     final DecimalFormatSymbols symbols; 
     final char     groupSeparatorChar; 
     final String    groupSeparator; 
     final char     decimalSeparatorChar; 
     final String    decimalSeparator; 
     String      fixedString; 
     final BigDecimal   number; 

     symbols    = new DecimalFormatSymbols(locale); 
     groupSeparatorChar = symbols.getGroupingSeparator(); 
     decimalSeparatorChar = symbols.getDecimalSeparator(); 

     if(groupSeparatorChar == '.') 
     { 
      groupSeparator = "\\" + groupSeparatorChar; 
     } 
     else 
     { 
      groupSeparator = Character.toString(groupSeparatorChar); 
     } 

     if(decimalSeparatorChar == '.') 
     { 
      decimalSeparator = "\\" + decimalSeparatorChar; 
     } 
     else 
     { 
      decimalSeparator = Character.toString(decimalSeparatorChar); 
     } 

     fixedString = formattedString.replaceAll(groupSeparator , ""); 
     fixedString = fixedString.replaceAll(decimalSeparator , "."); 
     number  = new BigDecimal(fixedString); 

     return (number); 
    } 
} 
18

Il seguente codice di esempio funziona bene (locale deve essere ottenuto in modo dinamico)

import java.math.BigDecimal; 
import java.text.NumberFormat; 
import java.text.DecimalFormat; 
import java.text.ParsePosition; 
import java.util.Locale; 

class TestBigDecimal { 
    public static void main(String[] args) { 

     String str = "0,00"; 
     Locale in_ID = new Locale("in","ID"); 
     //Locale in_ID = new Locale("en","US"); 

     DecimalFormat nf = (DecimalFormat)NumberFormat.getInstance(in_ID); 
     nf.setParseBigDecimal(true); 

     BigDecimal bd = (BigDecimal)nf.parse(str, new ParsePosition(0)); 

     System.out.println("bd value : " + bd); 
    } 
} 
0

argomento vecchio, ma forse il più facile è quello di utilizzare Apache Commons NumberUtils che ha un metodo createBigDecimal (String value). ...

Immagino (spero) tenga conto delle impostazioni locali altrimenti sarebbe piuttosto inutile.

+0

createBigDecimal è solo un wrapper per il nuovo BigDecimal (stringa), come indicato nel [Codice sorgente NumberUtils] (https://commons.apache.org/proper/commons -lang/javadocs/api-3.1/src-html/org/apache/commons/lang3/math/NumberUtils.html # line.709) che non considera Locale AFAIK –

0

Si prega di provare questo suo lavoro per me

BigDecimal bd ; 
String value = "2000.00"; 

bd = new BigDecimal(value); 
BigDecimal currency = bd; 
+1

In questo modo non supporta la specifica dei delimitatori personalizzati per il punto decimale e per le mani gruppi. – bezmax

+0

Sì, ma questo è per ottenere il valore BigDecimal della stringa da oggetto come HashMap e la memorizzazione in valore BigDecimal per chiamare gli altri servizi –

+1

Sì, ma non era questa la domanda. – bezmax

2

avevo bisogno di una soluzione per convertire una stringa in un BigDecimal senza conoscere il locale e di essere locale indipendente. Non sono riuscito a trovare alcuna soluzione standard per questo problema, quindi ho scritto il mio metodo di supporto. Può essere utile anche a chiunque altro:

Aggiornamento: Avviso! Questo metodo di supporto funziona solo per i numeri decimali, quindi i numeri che hanno sempre un punto decimale! In caso contrario, il metodo di supporto potrebbe fornire risultati errati per numeri compresi tra 1000 e 999999 (più/meno). Grazie a bezmax per il suo grande contributo!

/** 
    * Converts a String to a BigDecimal. 
    *  if there is more than 1 '.', the points are interpreted as thousand-separator and will be removed for conversion 
    *  if there is more than 1 ',', the commas are interpreted as thousand-separator and will be removed for conversion 
    * the last '.' or ',' will be interpreted as the separator for the decimal places 
    * () or - in front or in the end will be interpreted as negative number 
    * 
    * @param value 
    * @return The BigDecimal expression of the given string 
    */ 
    public static BigDecimal toBigDecimal(final String value) { 
     if (value != null){ 
      boolean negativeNumber = false; 

      if (value.containts"(") && value.contains(")") 
       negativeNumber = true; 
      if (value.endsWith("-") || value.startsWith("-")) 
       negativeNumber = true; 

      String parsedValue = value.replaceAll("[^0-9\\,\\.], EMPTY); 

      if (negativeNumber) 
       parsedValue = "-" + parsedValue; 

      int lastPointPosition = parsedValue.lastIndexOf(POINT); 
      int lastCommaPosition = parsedValue.lastIndexOf(COMMA); 

      //handle '1423' case, just a simple number 
      if (lastPointPosition == -1 && lastCommaPosition == -1) 
       return new BigDecimal(parsedValue); 
      //handle '45.3' and '4.550.000' case, only points are in the given String 
      if (lastPointPosition > -1 && lastCommaPosition == -1){ 
       int firstPointPosition = parsedValue.indexOf(POINT); 
       if (firstPointPosition != lastPointPosition) 
        return new BigDecimal(parsedValue.replace(POINT_AS_STRING, EMPTY)); 
       else 
        return new BigDecimal(parsedValue); 
      } 
      //handle '45,3' and '4,550,000' case, only commas are in the given String 
      if (lastPointPosition == -1 && lastCommaPosition > -1){ 
       int firstCommaPosition = parsedValue.indexOf(COMMA); 
       if (firstCommaPosition != lastCommaPosition) 
        return new BigDecimal(parsedValue.replace(COMMA_AS_STRING, EMPTY)); 
       else 
        return new BigDecimal(parsedValue.replace(COMMA, POINT)); 
      } 
      //handle '2.345,04' case, points are in front of commas 
      if (lastPointPosition < lastCommaPosition){ 
       parsedValue = parsedValue.replace(POINT_AS_STRING, EMPTY); 
       return new BigDecimal(parsedValue.replace(COMMA, POINT)); 
      } 
      //handle '2,345.04' case, commas are in front of points 
      if (lastCommaPosition < lastPointPosition){ 
       parsedValue = parsedValue.replace(COMMA_AS_STRING, EMPTY); 
       return new BigDecimal(parsedValue); 
      } 
      throw new NumberFormatException("Unexpected number format. Cannot convert '" + value + "' to BigDecimal."); 
     } 
     return null; 
    } 

Naturalmente ho testato il metodo:

@Test(dataProvider = "testBigDecimals") 
    public void toBigDecimal_defaultLocaleTest(String stringValue, BigDecimal bigDecimalValue){ 
     BigDecimal convertedBigDecimal = DecimalHelper.toBigDecimal(stringValue); 
     Assert.assertEquals(convertedBigDecimal, bigDecimalValue); 
    } 
    @DataProvider(name = "testBigDecimals") 
    public static Object[][] bigDecimalConvertionTestValues() { 
     return new Object[][] { 
       {"5", new BigDecimal(5)}, 
       {"5,3", new BigDecimal("5.3")}, 
       {"5.3", new BigDecimal("5.3")}, 
       {"5.000,3", new BigDecimal("5000.3")}, 
       {"5.000.000,3", new BigDecimal("5000000.3")}, 
       {"5.000.000", new BigDecimal("5000000")}, 
       {"5,000.3", new BigDecimal("5000.3")}, 
       {"5,000,000.3", new BigDecimal("5000000.3")}, 
       {"5,000,000", new BigDecimal("5000000")}, 
       {"+5", new BigDecimal("5")}, 
       {"+5,3", new BigDecimal("5.3")}, 
       {"+5.3", new BigDecimal("5.3")}, 
       {"+5.000,3", new BigDecimal("5000.3")}, 
       {"+5.000.000,3", new BigDecimal("5000000.3")}, 
       {"+5.000.000", new BigDecimal("5000000")}, 
       {"+5,000.3", new BigDecimal("5000.3")}, 
       {"+5,000,000.3", new BigDecimal("5000000.3")}, 
       {"+5,000,000", new BigDecimal("5000000")}, 
       {"-5", new BigDecimal("-5")}, 
       {"-5,3", new BigDecimal("-5.3")}, 
       {"-5.3", new BigDecimal("-5.3")}, 
       {"-5.000,3", new BigDecimal("-5000.3")}, 
       {"-5.000.000,3", new BigDecimal("-5000000.3")}, 
       {"-5.000.000", new BigDecimal("-5000000")}, 
       {"-5,000.3", new BigDecimal("-5000.3")}, 
       {"-5,000,000.3", new BigDecimal("-5000000.3")}, 
       {"-5,000,000", new BigDecimal("-5000000")}, 
       {null, null} 
     }; 
    } 
+1

Mi spiace, ma non vedo come ciò possa essere utile a causa dei casi limite. Ad esempio, come fai a sapere cosa rappresenta "7.333"? È 7333 o 7 e 1/3 (7.333)? In diverse versioni locali tale numero sarebbe analizzato in modo diverso. Ecco la dimostrazione del concetto: http://ideone.com/Ue9rT8 – bezmax

+0

Un buon input, non ha mai avuto questo problema nella pratica. Aggiornerò il mio post, grazie mille! E migliorerò i test per questi speciali locali solo per sapere dove riceveranno problemi. – user3227576

+1

Ho verificato la soluzione per diverse versioni locali e numeri diversi ... e per noi tutto funziona, perché abbiamo sempre numeri con un punto decimale (stiamo leggendo i valori di Money da un file di testo). Ho aggiunto un avvertimento alla risposta. – user3227576