Cara terbaik untuk parseDouble dengan koma sebagai pemisah desimal?


148

Berikut ini menghasilkan Exception:

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

Apakah ada cara yang lebih baik untuk menguraikan "1,234"untuk mendapatkan 1.234daripada p = p.replaceAll(",",".");:?


17
Dalam pengalaman saya, replaceAll (), seperti yang Anda sarankan, adalah cara terbaik untuk melakukan ini. Itu tidak tergantung pada lokal saat ini, itu sederhana, dan itu berfungsi.
Joonas Pulakka

1
@Marco Altieri: replaceAll(",",".")mengganti semua koma dengan titik. Jika tidak ada koma, maka tidak melakukan apa pun. Double.valueOf()berfungsi (hanya) dengan string yang menggunakan titik sebagai pemisah desimal. Tidak ada yang terpengaruh oleh lokal default saat ini. docs.oracle.com/javase/8/docs/api/java/lang/…
Joonas Pulakka

4
Satu-satunya masalah replaceAll(",",".")adalah bahwa itu hanya akan berfungsi jika ada satu koma: yaitu: 1.234.567 akan melempar java.lang.NumberFormatException: multiple points. Sebuah regex dengan lookahead positif akan mencukupi p.replaceAll(",(?=[0-9]+,)", "").replaceAll(",", ".")More at: regular-expressions.info/lookaround.html
artemisian

2
Tidak ada masalah. NumberFormatException baik. Bagaimana Anda bisa tahu koma mana yang tepat? Formatnya salah dan yang bisa Anda lakukan hanyalah menampilkan pesan yang bisa dibaca lebih baik daripada pengecualian bagi pengguna.
Luar biasa

2
@TheincredibleJan Tidak, formatnya tidak salah. Beberapa lokal menggunakan koma sebagai pemisah ribuan, sehingga Anda dapat memiliki lebih dari satu dari mereka dalam suatu angka dan secara teknis masih merupakan input yang valid.
Vratislav Jindra

Jawaban:


206

Gunakan java.text.NumberFormat :

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

11
Ini hanya berfungsi jika lokal default saat ini menggunakan koma sebagai pemisah desimal.
Joonas Pulakka

6
Untuk lebih mengacaukan segalanya, beberapa lokal menggunakan koma sebagai pemisah ribuan , dalam hal ini "1.234" akan diuraikan menjadi 1.234.0 bukannya melemparkan kesalahan.
Joonas Pulakka

17
Masalahnya dengan NumberFormat adalah ia akan mengabaikan karakter yang tidak valid secara diam-diam. Jadi, jika Anda mencoba menguraikan "1,23abc", ia akan dengan senang hati mengembalikan 1,23 tanpa menunjukkan kepada Anda bahwa String yang lewat berisi karakter yang tidak dapat diuraikan. Dalam beberapa situasi yang mungkin sebenarnya diinginkan, tetapi saya tidak berpikir itu biasanya perilaku yang diinginkan.
E-Riz

7
untuk TURKEY, Anda harus menggunakan NumberFormat.getInstance (lokal baru (tr_TR))
Günay Gültekin

2
untuk yang menggunakan apa yang
pemisah

67

Anda dapat menggunakan ini (bahasa Prancis memiliki ,pemisah desimal)

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

Atau Anda dapat menggunakan java.text.DecimalFormatdan mengatur simbol yang sesuai:

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

Ya ... jika kita tidak mengatur pemisah ribuan pengelompokan dan hanya menggunakan format Prancis, angka dalam format Spanyol (1.222.222,33) akan dikonversi menjadi "1 222 222,33", yang bukan yang saya inginkan . Jadi terima kasih!
WesternGun

1
Hal lain adalah, lokal Spanyol tidak terdaftar sebagai "default` dan saya tidak dapat membangun Localedengan format yang benar dengan new Locale("es", "ES")dan kemudian secara otomatis mengurai string angka dengan NumberFormat, dengan ,pemisah desimal dan .sebagai pemisah ribuan grup. Hanya DecimalFormatberfungsi.
WesternGun

Mengapa tidak semua negara tersedia di sana? Saya merasa aneh menggunakan lokal Prancis untuk memformat angka Polandia ...
Baris

18

Seperti yang ditunjukkan E-Riz, NumberFormat.parse (String) mengurai "1,23abc" sebagai 1,23. Untuk mengambil seluruh input yang bisa kita gunakan:

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
Strategi ini dijelaskan secara rinci di sini: ibm.com/developerworks/library/j-numberformat
Janus Varmarken

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

... sangat cepat karena mencari array karakter yang mendasarinya berdasarkan char-by-char. String yang menggantikan versi menyusun RegEx untuk dievaluasi.

Pada dasarnya ganti (char, char) sekitar 10 kali lebih cepat dan karena Anda akan melakukan hal-hal semacam ini dalam kode tingkat rendah, masuk akal untuk memikirkan hal ini. Pengoptimal Hot Spot tidak akan menemukannya ... Pasti tidak ada di sistem saya.


4

Jika Anda tidak tahu Lokal yang benar dan string dapat memiliki seribu pemisah, ini bisa menjadi pilihan terakhir:

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

Menyadari: ini dengan senang hati akan mengurai string seperti "R 1 52.43,2" menjadi "15243.2".


4

Ini adalah metode statis yang saya gunakan dalam kode saya sendiri:

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
Bagaimana jika Lokal default adalah sesuatu seperti Jerman, di mana koma menunjukkan tempat desimal? Anda dapat memasukkan, misalnya "1.000.000" yang tidak akan diuraikan ke dalam Bahasa Jerman Lokal dan kemudian akan diganti dengan "1.000.000" yang bukan merupakan Double yang valid.
Eddie Curtis

Hai @ jimmycar, saya baru saja memperbarui jawaban saya untuk menggunakan versi metode statis saya saat ini. Saya harap ini menyelesaikan masalah Anda! Pete
Pete


0

Dalam kasus di mana Anda tidak tahu lokal dari nilai string yang diterima dan itu tidak selalu sama dengan lokal default saat ini, Anda dapat menggunakan ini:

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

}

Ini akan mengembalikan ganda tidak peduli apa lokal dari string itu. Dan tidak peduli berapa banyak koma atau poin. Jadi passing 1,000,000.54akan bekerja sehingga 1.000.000,54Anda tidak perlu bergantung pada lokal default untuk mengurai string lagi. Kode tidak seoptimal mungkin sehingga saran dapat diterima. Saya mencoba menguji sebagian besar kasus untuk memastikan itu menyelesaikan masalah tetapi saya tidak yakin itu mencakup semuanya. Jika Anda menemukan nilai yang berharga, beri tahu saya.


-5

Ini akan melakukan pekerjaan:

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

6
Pertanyaan awal mengatakan "Apakah ada cara yang lebih baik untuk mengurai" 1.234 "untuk mendapatkan 1.234 daripada: p = p.replaceAll (", ",". ");" , jika menurut Anda replaceberbeda secara signifikan dari penggunaan replaceAll, harap jelaskan alasannya.
SuperBiasedMan
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.