2010-09-20 21 views
95

Estoy tratando de leer algunos valores BigDecimal de la cadena. Digamos que tengo esta cadena: "1,000,000,000.999999999999999" y quiero obtener un BigDecimal de ella. ¿Cuál es la manera de hacerlo?Safe String to BigDecimal conversion

Antes que nada, no me gustan las soluciones que usan cadenas reemplazadas (reemplazando comas, etc.). Creo que debería haber algún formateador aseado para hacer ese trabajo por mí.

He encontrado una clase DecimalFormatter, sin embargo, como funciona a través de doble, se pierden enormes cantidades de precisión.

Entonces, ¿cómo puedo hacerlo?

+2

Por qué no String.replaceAll? – TofuBeer

+0

Dado que se le da un formato personalizado, es complicado convertir el formato en formato compatible con BigDecimal. – bezmax

+2

* "Dado que tiene un formato personalizado, es un dolor ..." * No sé, separa los dominios problemáticos. Primero, limpie las cadenas de caracteres legibles por el ser humano y luego dele la mano a algo que sepa cómo convertir el resultado de forma correcta y eficiente en un 'BigDecimal'. –

Respuesta

79

Consulte setParseBigDecimal en DecimalFormat. Con este setter, parse le devolverá un BigDecimal.

+1

El constructor en la clase BigDecimal no admite formatos personalizados. El ejemplo que di en la pregunta usa el formato personalizado (comas). No se puede analizar eso con BigDecimal usando su constructor. – bezmax

+23

Si va a ** cambiar ** completamente su respuesta, le sugiero que lo mencione en la respuesta. De lo contrario, parece muy extraño cuando las personas han señalado por qué su respuesta original no tenía ningún sentido. :-) –

+1

Sí, no noté ese método. Gracias, parece ser la mejor manera de hacerlo. Voy a probarlo ahora y si funciona correctamente, acepta la respuesta. – bezmax

3

Aquí es cómo lo haría:

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

Obviamente, si esto se va en el código de producción, no sería tan sencillo.

No veo ningún problema simplemente eliminando las comas del String.

+0

¿Y si la cadena no está en formato americano? En Alemania, sería de 1,000,000,000,999999999999999 –

+0

@Steve, buena llamada – jjnguy

+2

Así que utiliza la configuración regional para limpiarla de las cosas correctas. –

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

código completo de comprobar que no se lanza NumberFormatException:

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

salida

1000000000,999999999999999

+0

@Steve McLeod .... no, acabo de probarlo ... mi valor es '1000000000.999999999999999' –

+7

Pruebe con la configuración regional ALEMANA, y 1.000.000.000,999999999999 – TofuBeer

+0

@ # TofuBeer .... el ejemplo fue 1,000,000,000.999999999999999.Si tuviera que hacer tu locale, tendría que reemplazar todos los puntos al espacio ... –

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

eliminará todos los caracteres excepto los dígitos y el punto de su cadena.

Para que sea compatible con la configuración regional, es posible que desee utilizar getDecimalSeparator() en java.text.DecimalFormatSymbols. No sé de Java, pero podría tener este aspecto:

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

Lo que dará resultados inesperados para los números no estadounidenses: vea los comentarios a la respuesta de Justin. –

+0

Lo sé, solo estaba editando mi respuesta cuando comentó :) –

5

El código podría estar más limpio, pero esto parece hacer el truco para diferentes lugares.

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

El siguiente código de ejemplo funciona bien (necesidad entorno local que ha obtenido de forma dinámica)

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

viejo tema, pero tal vez la más sencilla es utilizar Apache Commons NumberUtils que tiene una createBigDecimal método (Valor de la cadena). ...

Supongo (espero) que tenga en cuenta los locales o de lo contrario sería bastante inútil.

+0

createBigDecimal es solo un contenedor para el nuevo BigDecimal (cadena), como se indica en el [Código fuente de NumberUtils] (https://commons.apache.org/proper/commons -lang/javadocs/api-3.1/src-html/org/apache/commons/lang3/math/NumberUtils.html # line.709) que no considera la configuración regional AFAIK –

0

Inténtelo este su trabajo para mí

BigDecimal bd ; 
String value = "2000.00"; 

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

De esta manera no es compatible con la especificación de delimitadores personalizados para punto decimal y thouthands grupos. – bezmax

+0

Sí, pero esto es para obtener el valor de cadena BigDecimal de un objeto como HashMap y almacenarlo en un valor Bigdecimal para llamar a los otros servicios –

+1

Sí, pero esa no era la pregunta. – bezmax

2

que necesitaba una solución para convertir una cadena a un BigDecimal sin conocer la configuración regional y de ser independiente de la Localidad. No pude encontrar ninguna solución estándar para este problema, así que escribí mi propio método de ayuda. Puede ser que ayude a alguien más también:

Actualización: ¡Advertencia! Este método de ayuda solo funciona para números decimales, ¡por lo que los números siempre tienen un punto decimal! De lo contrario, el método auxiliar podría arrojar un resultado incorrecto para números entre 1000 y 999999 (más/menos). ¡Gracias a Bezmax por su gran contribución!

/** 
    * 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; 
    } 

Por supuesto que he probado el método:

@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

Lo siento, pero no veo cómo esto puede ser útil debido a casos límite. Por ejemplo, ¿cómo sabes qué representa '7,333'? ¿Es 7333 o 7 y 1/3 (7.333)? En diferentes configuraciones regionales, ese número se analizaría de manera diferente. Aquí hay una prueba de concepto: http://ideone.com/Ue9rT8 – bezmax

+0

Buena entrada, nunca tuve este problema en la práctica. Actualizaré mi publicación, ¡muchas gracias! Y mejoraré las pruebas para estas ofertas de Locale solo para saber dónde recibirán problemas. – user3227576

+1

He comprobado la solución para diferentes configuraciones regionales y diferentes números ... y para todos nosotros funciona, porque siempre tenemos números con un punto decimal (estamos leyendo valores monetarios de un archivo de texto). He agregado una advertencia a la respuesta. – user3227576