2010-09-20 5 views
95

Я пытаюсь прочитать некоторые значения BigDecimal из строки. Скажем, у меня есть эта строка: «1,000,000,000.9999999999999999», и я хочу получить BigDecimal. Каким образом это сделать?Безопасная строка для преобразования BigDecimal

Прежде всего, мне не нравятся решения, использующие замену строк (замена запятых и т. Д.). Я думаю, что для меня эта работа должна быть аккуратной.

Я нашел класс DecimalFormatter, однако, поскольку он работает через double - огромное количество точности теряется.

Итак, как я могу это сделать?

+2

Почему нет String.replaceAll? – TofuBeer

+0

Потому что, учитывая какой-то пользовательский формат, больно заставить его преобразовать ваш формат в формат BigDecimal. – bezmax

+2

* «Потому что, учитывая какой-то пользовательский формат, это боль ...» * Я не знаю, это отделяет проблемные домены. Сначала вы очищаете удобочитаемый материал из строки, а затем передаете то, что знает, как правильно и эффективно превращать результат в «BigDecimal». –

ответ

79

Отъезд setParseBigDecimal in DecimalFormat. С этим установщиком parse вернет вам BigDecimal.

+1

Конструктор в классе BigDecimal не поддерживает пользовательские форматы. В примере, который я дал в вопросе, используется пользовательский формат (запятые). Вы не можете разобрать это в BigDecimal, используя его конструктор. – bezmax

+23

Если вы собираетесь ** полностью ** изменить свой ответ, я бы предложил упомянуть об этом в ответе. Иначе это выглядит очень странно, когда люди указывают, почему ваш первоначальный ответ не имеет никакого смысла. :-) –

+1

Да, не заметил этого метода. Спасибо, это лучший способ сделать это. Собираюсь проверить его сейчас, и если он будет работать правильно - примите ответ. – bezmax

3

Вот как я бы это сделать:

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

Очевидно, что если бы это было идти в производство кода, это будет не так просто.

Я не вижу проблемы, просто удаляя запятые из строки.

+0

А если строка не в американском формате? В Германии это будет 1.000.000.000,999999999999999 –

+0

@Steve, хороший звонок – jjnguy

+2

Итак, вы используете локаль, чтобы очистить ее от правильных вещей. –

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

Полный код доказать, что ни 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); 
    } 
} 

Выход

1000000000,999999999999999

+0

@Steve McLeod .... нет, я только что проверил его .... моя ценность '1000000000.999999999999999' –

+7

Попробуйте с немецким языком и 1.000.000.000,999999999999 – TofuBeer

+0

@ # TofuBeer .... пример был 1,000,000,000.999999999999999.Если бы мне пришлось сделать ваш язык, мне пришлось бы заменить все точки на космос. –

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

удалит все символы, кроме цифр и точки из вашей строки.

Чтобы сделать его доступным для локализации, вы можете использовать getDecimalSeparator() от java.text.DecimalFormatSymbols. Я не знаю, Java, но это может выглядеть следующим образом:

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

Это даст неожиданные результаты для неамериканских номеров - см. Комментарии к ответу Джастина. –

+0

Я знаю, просто редактировал мой ответ, когда вы прокомментировали :) –

5

код может быть чище, но это, кажется, сделать трюк для различных языков.

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

Следующий пример кода работает хорошо (локаль должны быть получены динамически)

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

Старая тема, но, возможно, самый простой в использовании Apache Commons NumberUtils, который имеет метод createBigDecimal (значение строки). ...

Я предполагаю (надеюсь), что он учитывает локали, иначе это было бы бесполезно.

+0

createBigDecimal - это только оболочка для новой BigDecimal (строка), как указано в [NumberUtils Исходный код] (https://commons.apache.org/proper/commons -lang/javadocs/api-3.1/src-html/org/apache/commons/lang3/math/NumberUtils.html # строка.709), который не рассматривает Locale AFAIK –

0

Пожалуйста, попробуйте эту свою работу для меня

BigDecimal bd ; 
String value = "2000.00"; 

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

Этот способ не поддерживает указание пользовательских разделителей для десятичной точки и thouthands групп. – bezmax

+0

Да, но это для получения строки BigDecimal значения от объекта, такого как HashMap, и сохранения в Bigdecimal значение для вызова других служб. –

+1

Да, но это был не вопрос. – bezmax

2

мне нужно было решение, чтобы преобразовать строку в BigDecimal, не зная, локаль и быть независимым от языковых настроек. Я не мог найти стандартное решение для этой проблемы, поэтому я написал свой собственный вспомогательный метод. Может быть, это тоже помогает кому-то еще:

Обновление: Внимание! Этот вспомогательный метод работает только для десятичных чисел, поэтому числа, которые всегда имеют десятичную точку! В противном случае вспомогательный метод может привести к неправильному результату для чисел от 1000 до 999999 (плюс/минус). Благодаря bezmax за его большой вклад!

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

Конечно я опробованные методы:

@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

Извините, но я не вижу, как это может быть полезно из-за краев. Например, откуда вы знаете, что означает '7,333'? Это 7333 или 7 и 1/3 (7.333)? В разных локалях это число будет анализироваться по-разному. Вот доказательство концепции: http://ideone.com/Ue9rT8 – bezmax

+0

Хороший вклад, никогда не было этой проблемы на практике. Я обновлю свой пост, спасибо вам большое! И я улучшу тестирование этих специальных предложений Locale, чтобы знать, где будут получать неприятности. – user3227576

+1

Я проверил решение для разных локалей и разных номеров ... и для нас все работает, потому что у нас всегда есть числа с десятичной точкой (мы читаем денежные значения из текстового файла). Я добавил предупреждение. – user3227576

Смежные вопросы