Cadeia de caracteres segura para conversão BigDecimal

Eu estou tentando ler alguns valores BigDecimal da string. Digamos que eu tenha este String: “1,000,000,000.999999999999999” e eu quero tirar um BigDecimal dele. Qual é a maneira de fazer isso?

Primeiro de tudo, eu não gosto das soluções usando substituições de string (substituindo vírgulas etc.). Eu acho que deveria haver algum formatador legal para fazer esse trabalho para mim.

Eu encontrei uma class DecimalFormatter, no entanto, como ela opera por meio de enormes quantidades de precisão são perdidas.

Então, como posso fazer isso?

Confira setParseBigDecimal em DecimalFormat. Com esse setter, o parse retornará um BigDecimal para você.

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

Código completo para provar que nenhuma NumberFormatException é lançada:

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

Saída

1000000000.999999999999999

O código de exemplo a seguir funciona bem (o código do idioma precisa ser obtido dinamicamente)

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

O código poderia ser mais limpo, mas isso parece fazer o truque para diferentes localidades.

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

Aqui está como eu faria:

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

Obviamente, se isso estivesse acontecendo no código de produção, não seria tão simples assim.

Não vejo problema em simplesmente remover as vírgulas da String.

Eu precisava de uma solução para converter um String em um BigDecimal sem conhecer o local e ser independente do código de idioma. Não encontrei nenhuma solução padrão para esse problema, então escrevi meu próprio método auxiliar. Pode ser que ajude alguém também:

Atualização: aviso! Este método auxiliar funciona apenas para números decimais, portanto, números que sempre têm um ponto decimal! Caso contrário, o método auxiliar poderia fornecer um resultado errado para números entre 1000 e 999999 (mais / menos). Graças a bezmax por sua grande input!

 static final String EMPTY = ""; static final String POINT = '.'; static final String COMMA = ','; static final String POINT_AS_STRING = "."; static final String COMMA_AS_STRING = ","; /** * 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; } 

Claro que testei o 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} }; } 
 resultString = subjectString.replaceAll("[^.\\d]", ""); 

irá remover todos os caracteres, exceto dígitos e o ponto da sua string.

Para torná-lo ciente do getDecimalSeparator() idioma, você pode querer usar getDecimalSeparator() em java.text.DecimalFormatSymbols . Eu não sei Java, mas pode parecer assim:

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

Tópico antigo, mas talvez o mais fácil é usar o NumberUtils do Apache, que possui um método createBigDecimal (valor String) ….

Eu acho que (espero) leva em conta locales ou então seria bastante inútil.

Por favor, tente isso está funcionando para mim

 BigDecimal bd ; String value = "2000.00"; bd = new BigDecimal(value); BigDecimal currency = bd;