Найкращий спосіб розборуDouble з комою як десятковим роздільником?


148

Наступне призводить до Exception:

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

Чи існує кращий спосіб розбору, ніж "1,234"отримати :?1.234p = p.replaceAll(",",".");


17
На мій досвід, замість AllAll (), як ви запропонували, це найкращий спосіб зробити це. Це не залежить від поточного локалу, це просто, і він працює.
Joonas Pulakka

1
@Marco Altieri: replaceAll(",",".")замінює всі коми крапками. Якщо немає коми, то це нічого не робить. Double.valueOf()працює (лише) з рядками, які використовують крапку як десятковий роздільник. Ніщо тут не впливає на поточну локальну мову. docs.oracle.com/javase/8/docs/api/java/lang/…
Joonas Pulakka

4
Єдина проблема в replaceAll(",",".")тому, що вона працюватиме лише за наявності однієї коми: тобто: 1,234,567 кине java.lang.NumberFormatException: multiple points. Регексу з позитивним знаком буде достатньо p.replaceAll(",(?=[0-9]+,)", "").replaceAll(",", ".")Детальніше за адресою: regular-expressions.info/lookaround.html
артемізієць

2
Немає жодних проблем. NumberFormatException хороший. Як ви можете знати, яка кома є правильною? Формат неправильний, і все, що ви можете зробити, це показати краще читабельне повідомлення, ніж виняток для користувача.
Неймовірний

2
@TheincredibleJan Ні, формат неправильний. Деякі локалі використовують кома як роздільник тисяч, тож ви можете мати декілька з них у кількості, а технічно це все-таки дійсний ввід.
Вратислав Джіндра

Відповіді:


206

Використовуйте java.text.NumberFormat :

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

11
Це працює лише в тому випадку, якщо в поточному локалі за замовчуванням використовується кома як десятковий роздільник.
Joonas Pulakka

6
Для подальшого збиття з ладу деякі локали використовують коми як роздільник тисяч , і в цьому випадку "1,234" буде розбиратись на 1234,0 замість того, щоб видавати помилку.
Joonas Pulakka

17
Проблема з NumberFormat полягає в тому, що він мовчки ігнорує недійсні символи. Тож якщо ви спробуєте розібрати "1,23abc", він із задоволенням поверне 1,23, не вказуючи вам, що рядок, що передається, містить символи, які не можна проаналізувати. У деяких ситуаціях, які насправді можуть бути бажаними, але я не думаю, що це зазвичай бажана поведінка.
E-Riz

7
для ТУРЦІЇ ви повинні використовувати NumberFormat.getInstance (новий Locale (tr_TR))
Günay Gültekin

2
для того, хто використовує те, що використовує сепаратор, див. en.wikipedia.org/w/…
fiffy

67

Ви можете використовувати це (у французькій мові є ,десятковий роздільник)

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

Так ... якщо ми не встановимо роздільник тисяч угруповань і просто використаємо французький формат, число в іспанському форматі (1.222.222,33) буде перетворено на "1 222 222,33", що не є тим, що я хочу . Тож дякую!
WesternGun

1
Інша справа, що іспанська мова не вказана як "за замовчуванням", і я не можу побудувати Localeправильний формат з, new Locale("es", "ES")а потім автоматично розібрати рядок числа з NumberFormat, ,як десятковий роздільник і .як роздільник групи тисяч. Тільки DecimalFormatпрацює.
WesternGun

Чому не всі країни доступні там? Мені здається дивним використання французької мови для форматування польських номерів ...
Рядок

18

Як вказує 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();
}

2
Ця стратегія детально пояснюється тут: ibm.com/developerworks/library/j-numberformat
Janus Varmarken

7
Double.parseDouble(p.replace(',','.'))

... дуже швидкий, оскільки він шукає основний масив символів на основі принципу "чар-чар-чар". Версії для заміни рядків складають RegEx для оцінки.

В основному заміна (char, char) приблизно в 10 разів швидша, і оскільки ви робите такі речі в коді низького рівня, є сенс задуматися над цим. Оптимізатор гарячих точок цього не зрозуміє ... Звичайно, у моїй системі.


4

Якщо ви не знаєте правильний 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".


4

Це статичний метод, який я використовую у власному коді:

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

5
Що робити, якщо Locale за замовчуванням є чимось на зразок німецької, де комою позначається десятковий знак? Ви можете ввести, наприклад, "1,000,000", який би не розбирався на німецький Locale, і був би замінений на "1.000.000", що не є дійсним Double.
Едді Кертіс

Привіт @jimmycar, я тільки що оновив свою відповідь, щоб використовувати поточну версію мого статичного методу. Я сподіваюся, що це вирішить вашу проблему! Піт
Піт

1

Звичайно, потрібно використовувати правильну локаль. Це питання допоможе.


0

У тому випадку, коли ви не знаєте локаль отриманого значення рядка і це не обов'язково той самий локал, що і поточний локал за замовчуванням, ви можете використовувати це:

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тож вам більше не доведеться покладатися на локальний код за замовчуванням для розбору рядка. Код не настільки оптимізований, як може бути, тому будь-які пропозиції вітаються. Я намагався перевірити більшість випадків, щоб переконатися, що це вирішує проблему, але я не впевнений, що він охоплює всі. Якщо ви знайдете значення зламу, дайте мені знати.


-5

Це зробить цю роботу:

Double.parseDouble(p.replace(',','.')); 

6
Початкове запитання сказало: "Чи є кращий спосіб проаналізувати" 1,234 ", щоб отримати 1,234, ніж: p = p.replaceAll (", ",". ");" , якщо ви думаєте, що replaceзначно відрізняється від використання replaceAll, будь ласка, поясніть чому.
SuperBiasedMan
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.