Лучший способ разобрать Double с запятой в качестве десятичного разделителя?

Следующее приводит к Exception:

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

Есть ли лучший способ разобрать "1,234" получить 1.234 чем: p = p.replaceAll(",",".");?

8 ответов

Решение

Используйте java.text.NumberFormat:

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

Вы можете использовать это (французский язык имеет , для десятичного разделителя)

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

Или вы можете использовать java.text.DecimalFormat и установите соответствующие символы:

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

Как указывает E-Riz, NumberFormat.parse(String) анализирует "1,23abc" как 1,23. Чтобы взять весь ввод, мы можем использовать:

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();
}
Double.parseDouble(p.replace(',','.'))

... очень быстрый, так как он выполняет поиск в массиве символов на основе символьных символов. Строка замены версий компилирует RegEx для оценки.

Обычно замена (char,char) примерно в 10 раз быстрее, и, поскольку вы будете делать подобные вещи в низкоуровневом коде, имеет смысл подумать об этом. Оптимизатор Hot Spot не поймет этого... Конечно, не в моей системе.

Если вы не знаете правильный Locale и строка может иметь разделитель тысяч, это может быть последним средством:

    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(",", "."));

Имейте в виду: это с радостью проанализирует строки типа "R 1 52.43,2" до "15243.2".

Это статический метод, который я использую в своем собственном коде:

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://stackru.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;
        }
    }
}

В Kotlin вы можете использовать расширения, как показано ниже:

fun String.toDoubleEx() : Double {
   val decimalSymbol = DecimalFormatSymbols.getInstance().decimalSeparator
  return if (decimalSymbol == ',') {
      this.replace(decimalSymbol, '.').toDouble()
  } else {
      this.toDouble()
  }
}

и вы можете использовать его везде в своем коде следующим образом:

val myNumber1 = "5,2"
val myNumber2 = "6.7"

val myNum1 = myNumber1.toDoubleEx()
val myNum2 = myNumber2.toDoubleEx()

Это просто и универсально!

Вам, конечно, нужно использовать правильную локаль. Этот вопрос поможет.

В случае, если вы не знаете языковой стандарт полученного строкового значения, и это не обязательно тот же языковой стандарт, что и текущий языковой стандарт по умолчанию, вы можете использовать это:

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

}

Он вернет двойное значение независимо от языкового стандарта строки. И неважно, сколько там запятых или точек. Так проходя1,000,000.54 будет работать так будет 1.000.000,54так что вам больше не нужно полагаться на языковой стандарт по умолчанию для анализа строки. Код не так оптимизирован, как мог бы, поэтому любые предложения приветствуются. Я попытался протестировать большинство случаев, чтобы убедиться, что это решает проблему, но я не уверен, что он охватывает все. Если вы найдете критическое значение, дайте мне знать.

Это сделало бы работу:

Double.parseDouble(p.replace(',','.')); 
Другие вопросы по тегам