วิธีที่ดีที่สุดในการแยกคู่ด้วยเครื่องหมายจุลภาคเป็นตัวคั่นทศนิยม


148

การติดตามมีผลในException:

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

มีวิธีที่ดีกว่าที่จะแยก"1,234"ที่จะได้รับ1.234กว่า: p = p.replaceAll(",",".");?


17
จากประสบการณ์ของฉัน replaceAll () ตามที่คุณแนะนำเป็นวิธีที่ดีที่สุดในการทำเช่นนี้ มันไม่ได้ขึ้นอยู่กับสถานที่ปัจจุบันมันง่ายและใช้งานได้
Joonas Pulakka

1
@Marco Altieri: replaceAll(",",".")แทนที่เครื่องหมายจุลภาคทั้งหมดด้วยจุด หากไม่มีเครื่องหมายจุลภาคก็ไม่ทำอะไรเลย Double.valueOf()ทำงาน (เท่านั้น) กับสตริงที่ใช้จุดเป็นตัวแยกทศนิยม ไม่มีอะไรที่นี่ได้รับผลกระทบจากสถานที่เริ่มต้นปัจจุบัน docs.oracle.com/javase/8/docs/api/java/lang/…
Joonas Pulakka

4
ปัญหาเดียวที่มีreplaceAll(",",".")ก็คือว่ามันจะทำงานเฉพาะถ้ามีเครื่องหมายจุลภาคเดียว: เช่น: 1234567 java.lang.NumberFormatException: multiple pointsจะโยน regex ที่มี lookahead เป็นบวกจะพอเพียงp.replaceAll(",(?=[0-9]+,)", "").replaceAll(",", ".")เพิ่มเติมได้ที่: regular-expressions.info/lookaround.html
artemisian

2
ไม่มีปัญหา. NumberFormatException นั้นดี คุณจะรู้ได้อย่างไรว่าเครื่องหมายจุลภาคใดถูกต้อง รูปแบบไม่ถูกต้องและสิ่งที่คุณทำได้คือแสดงข้อความที่อ่านได้ดีกว่าข้อยกเว้นของผู้ใช้
เหลือเชื่อ

2
@TheincredibleJan ไม่รูปแบบไม่ผิด บางภาษาใช้เครื่องหมายจุลภาคเป็นตัวคั่นหลักพันดังนั้นคุณสามารถมีมากกว่าหนึ่งตัวในตัวเลขและในทางเทคนิคก็ยังเป็นอินพุตที่ถูกต้อง
Vratislav Jindra

คำตอบ:


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
สำหรับ TURKEY คุณควรใช้ NumberFormat.getInstance (สถานที่ใหม่ (tr_TR))
GünayGültekin

2
สำหรับผู้ที่ใช้สิ่งที่ seperator เห็น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
อีกสิ่งหนึ่งที่เป็นสถานที่สเปนไม่ได้ระบุว่าเป็น "default` และฉันไม่สามารถสร้าง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(',','.'))

... เร็วมากเพราะค้นหาอาเรย์ของอักขระที่อยู่บนพื้นฐานแบบ char-by-char สตริงแทนที่รุ่นรวบรวม RegEx เพื่อประเมิน

โดยทั่วไปการแทนที่ (char, char) จะเร็วกว่าประมาณ 10 เท่าและเนื่องจากคุณจะทำสิ่งเหล่านี้ในรหัสระดับต่ำมันจึงเหมาะสมที่จะคิดเกี่ยวกับสิ่งนี้ เครื่องมือเพิ่มประสิทธิภาพฮอตสปอตจะไม่คิดออก ... แน่นอนว่าไม่ได้อยู่ในระบบของฉัน


4

หากคุณไม่ทราบตำแหน่งที่ถูกต้องและสตริงสามารถมีตัวคั่นหลักพันได้นี่อาจเป็นทางเลือกสุดท้าย:

    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" ซึ่งจะไม่แยกวิเคราะห์ในภาษาเยอรมันและจะถูกแทนที่ด้วย "1.000.000" ซึ่งไม่ใช่ Double ที่ถูกต้อง
Eddie Curtis

สวัสดี @ jimmycar ฉันเพิ่งปรับปรุงคำตอบเพื่อใช้รุ่นปัจจุบันของวิธีการคงที่ของฉัน ฉันหวังว่านี่จะช่วยแก้ปัญหาของคุณ! Pete
Pete


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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.