Ondalık ayırıcı olarak virgülle ayrıştırmanın en iyi yolu?


151

Aşağıdakiler bir Exception:

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

Ayrıştırmak için daha iyi bir yolu var mı "1,234"almak 1.234daha: p = p.replaceAll(",",".");?


17
Tecrübelerime göre, önerdiğiniz gibi replaceAll (), bunu yapmanın en iyi yoludur. Mevcut yerel ayara bağlı değil, basit ve çalışıyor.
Joonas Pulakka

1
@Marco Altieri: replaceAll(",",".")tüm virgülleri noktalarla değiştirir. Virgül yoksa, hiçbir şey yapmaz. Double.valueOf()ondalık ayırıcı olarak nokta kullanan dizelerle çalışır (yalnızca). Buradaki hiçbir şey geçerli varsayılan yerel ayardan etkilenmez. docs.oracle.com/javase/8/docs/api/java/lang/…
Joonas Pulakka

5
Tek sorun, replaceAll(",",".")yalnızca tek bir virgül varsa işe yaramasıdır: yani: 1,234,567 atar java.lang.NumberFormatException: multiple points. Olumlu bakımlı bir normal ifade yeterli olacaktır p.replaceAll(",(?=[0-9]+,)", "").replaceAll(",", ".")Daha fazla bilgi için: normal-expressions.info/lookaround.html
artemisian

2
Sorun yok. NumberFormatException iyidir. Hangi virgülün doğru virgül olduğunu nasıl bilebilirsin? Biçim yanlış ve yapabileceğiniz tek şey, kullanıcıya istisnadan daha iyi okunabilir bir mesaj göstermektir.
inanılmaz

2
@TheincredibleJan Hayır, format yanlış değil. Bazı yerel ayarlar binlik ayırıcı olarak virgül kullanır, bu nedenle bir sayı içinde birden fazlasına sahip olabilirsiniz ve bu teknik olarak hala geçerli bir girdidir.
Vratislav Jindra

Yanıtlar:


208

Java.text.NumberFormat kullanın :

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

Güncellenmiş:

Çok dilli uygulamaları desteklemek için şunları kullanın:

NumberFormat format = NumberFormat.getInstance(Locale.getDefault());

11
Bu, yalnızca geçerli varsayılan yerel ayar ondalık ayırıcı olarak virgül kullanıyorsa işe yarar.
Joonas Pulakka

8
İşleri daha da karıştırmak için, bazı yerel ayarlar binlik ayırıcı olarak virgül kullanır , bu durumda "1.234" hata atmak yerine 1234.0 olarak ayrıştırılır.
Joonas Pulakka

18
NumberFormat ile ilgili sorun, geçersiz karakterleri sessizce yoksayacak olmasıdır. Dolayısıyla, "1,23abc" yi ayrıştırmaya çalışırsanız, size iletilen String'in ayrıştırılamaz karakterler içerdiğini belirtmeden mutlu bir şekilde 1.23 döndürür. Bazı durumlarda bu gerçekten arzu edilebilir, ancak bunun genellikle istenen davranış olduğunu düşünmüyorum.
E-Riz

7
TÜRKİYE için NumberFormat.getInstance (new Locale (tr_TR)) kullanmalısınız
Günay Gültekin

2
kim hangi
ayırıcıyı

67

Bunu kullanabilirsiniz (Fransız yerel ayarı ,ondalık ayırıcıya sahiptir)

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

Veya java.text.DecimalFormatuygun sembolleri kullanabilir ve ayarlayabilirsiniz:

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

Evet ... bin gruplama ayırıcısını ayarlamazsak ve sadece Fransızca biçimini kullanırsak, İspanyolca biçimindeki bir sayı (1.222.222,33) "1 222 222,33" e dönüştürülecek, bu benim istediğim şey değil . Çok teşekkürler!
WesternGun

1
Başka bir şey, İspanyolca yerel default`" olarak listelenmemiş ve ben bir inşa edemez Localeile doğru formatta ile new Locale("es", "ES")ve daha sonra otomatik olarak birlikte sayı dizesini ayrıştırmak NumberFormatile, ,ondalık olarak ayırıcı ve .bin grup ayırıcı olarak yalnızca. DecimalFormatÇalışır.
WesternGun

Neden tüm ülkeler orada mevcut değil? Lehçe sayıları biçimlendirmek için Fransızca yerel ayarı kullanmak konusunda kendimi tuhaf hissediyorum ...
Satır

19

E-Riz'in işaret ettiği gibi, NumberFormat.parse (String), "1,23abc" yi 1.23 olarak çözümler. Tüm girdiyi almak için kullanabiliriz:

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
Bu strateji burada ayrıntılı olarak açıklanmıştır: ibm.com/developerworks/library/j-numberformat
Janus Varmarken

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

... temel karakter dizisini karakter bazında aradığından çok hızlıdır. Dize değiştirme sürümleri, değerlendirmek için bir RegEx derler.

Temel olarak replace (char, char) yaklaşık 10 kat daha hızlıdır ve bu tür şeyleri düşük seviyeli kodda yapacağınız için bunun hakkında düşünmek mantıklıdır. Hot Spot iyileştirici bunu çözmeyecek ... Benim sistemimde kesinlikle yok.


4

Doğru Yerel Ayarı bilmiyorsanız ve dizede bin ayırıcı varsa, bu son çare olabilir:

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

Unutmayın: Bu, "R 1 52.43,2" ila "15243.2" gibi dizeleri mutlu bir şekilde ayrıştıracaktır.


4

Kendi kodumda kullandığım statik yöntem bu:

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

6
Varsayılan Yerel Ayar, virgülün ondalık basamağı gösterdiği Almanca gibi bir şeyse ne olur? Örneğin, Almanca Yerel Ayarına ayrıştırılmayan ve daha sonra geçerli bir İkili olmayan "1.000.000" ile değiştirilen "1.000.000" geçebilirsiniz.
Eddie Curtis

Merhaba @jimmycar, statik yöntemimin mevcut sürümünü kullanmak için cevabımı yeni güncelledim. Umarım bu sorununuzu çözer! Pete
Pete


0

Alınan dize değerinin yerel ayarını bilmediğiniz ve bunun geçerli varsayılan yerel ayar ile mutlaka aynı yerel ayar olmadığı durumlarda, bunu kullanabilirsiniz:

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

}

Dizenin yeri ne olursa olsun bir double döndürür. Ve kaç tane virgül veya nokta olduğu önemli değil. Bu yüzden geçiş yapmak 1,000,000.54da işe yarayacaktır, 1.000.000,54böylece artık dizeyi ayrıştırmak için varsayılan yerel ayara güvenmek zorunda kalmazsınız. Kod olabildiğince optimize edilmemiştir, bu nedenle herhangi bir öneriye açığız. Sorunu çözdüğünden emin olmak için vakaların çoğunu test etmeye çalıştım, ancak hepsini kapsadığından emin değilim. Bir kırılma değeri bulursanız bana bildirin.


0

Kotlin'de aşağıdaki uzantıları kullanabilirsiniz:

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

ve bunu kodunuzun her yerinde şu şekilde kullanabilirsiniz:

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

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

Kolay ve evrenseldir!


-5

Bu işi yapacaktı:

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

6
İlk soru, "1.234" ü 1.234 elde etmenin şundan daha iyi bir yolu var mıdır: p = p.replaceAll (",", "."); " , replacekullanmaktan önemli ölçüde farklı olduğunu düşünüyorsanız replaceAll, lütfen nedenini açıklayın.
SuperBiasedMan
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.