Cách tốt nhất để phân tích cú pháp với dấu phẩy là dấu phân cách thập phân?


148

Sau đây là kết quả Exception:

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

Có cách nào tốt hơn để phân tích "1,234"để có được 1.234hơn : p = p.replaceAll(",",".");?


17
Theo kinh nghiệm của tôi, thay thế All (), như bạn đề xuất, là cách tốt nhất để làm điều này. Nó không phụ thuộc vào ngôn ngữ hiện tại, nó đơn giản và nó hoạt động.
Joonas Pulakka

1
@Marco Altieri: replaceAll(",",".")thay thế tất cả các dấu phẩy bằng dấu chấm. Nếu không có dấu phẩy, thì nó không làm gì cả. Double.valueOf()hoạt động (chỉ) với các chuỗi sử dụng dấu chấm làm dấu tách thập phân. Không có gì ở đây bị ảnh hưởng bởi ngôn ngữ mặc định hiện tại. docs.oracle.com/javase/8/docs/api/java/lang/ triệt
Joonas

4
Vấn đề duy nhất replaceAll(",",".")là nó sẽ chỉ hoạt động nếu có một dấu phẩy duy nhất: tức là: 1,234,567 sẽ ném java.lang.NumberFormatException: multiple points. Một regex với cái nhìn tích cực sẽ đủ để biết p.replaceAll(",(?=[0-9]+,)", "").replaceAll(",", ".")thêm tại: normal-expressions.info/lookaround.html
artemisian

2
Không có vấn đề gì cả. NumberFormatException là tốt. Làm thế nào bạn có thể biết dấu phẩy nào là đúng? Định dạng sai và tất cả những gì bạn có thể làm là hiển thị một thông điệp dễ đọc hơn ngoại lệ cho người dùng.
Không thể tin được vào

2
@TheincredibleJan Không, định dạng không sai. Một số địa phương sử dụng dấu phẩy dưới dạng dấu phân cách hàng nghìn, vì vậy bạn có thể có nhiều hơn một trong số chúng trong một số và về mặt kỹ thuật vẫn là một đầu vào hợp lệ.
Vratislav Jindra

Câu trả lời:


206

Sử dụng java.text.NumberFormat :

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

11
Điều này chỉ hoạt động nếu ngôn ngữ mặc định hiện tại xảy ra để sử dụng dấu phẩy làm dấu tách thập phân.
Joonas Pulakka

6
Để làm rối thêm mọi thứ, một số địa phương sử dụng dấu phẩy như một dấu phân cách hàng nghìn , trong trường hợp đó, "1,234" sẽ phân tích thành 1234.0 thay vì ném lỗi.
Joonas Pulakka

17
Vấn đề với NumberFormat là nó sẽ âm thầm bỏ qua các ký tự không hợp lệ. Vì vậy, nếu bạn cố gắng phân tích "1,23abc", nó sẽ vui vẻ trả về 1.23 mà không cho bạn biết rằng Chuỗi được truyền có chứa các ký tự không phân tích được. Trong một số tình huống có thể thực sự mong muốn, nhưng tôi không nghĩ đó thường là hành vi mong muốn.
E-Riz

7
đối với Thổ Nhĩ Kỳ, bạn nên sử dụng NumberFormat.getInstance (địa điểm mới (tr_TR))
Günay Gültekin

2
đối với những người sử dụng những gì người chia sẻ, hãy xem en.wikipedia.org/w/ Từ
fiffy

67

Bạn có thể sử dụng cái này (miền địa phương của Pháp có ,dấu phân cách thập phân)

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

Hoặc bạn có thể sử dụng java.text.DecimalFormatvà đặt các ký hiệu thích hợp:

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

Có ... nếu chúng tôi không đặt dấu phân cách nhóm và chỉ sử dụng định dạng tiếng Pháp, một số ở định dạng tiếng Tây Ban Nha (1.222.222,33) sẽ được chuyển đổi thành "1 222 222,33", đó không phải là điều tôi muốn . Vì vậy, cảm ơn!
WesternGun

1
Một điều nữa là, miền địa phương Tây Ban Nha không được liệt kê là "mặc định" và tôi không thể xây dựng một Localeđịnh dạng chính xác với new Locale("es", "ES")và sau đó tự động phân tích chuỗi số bằng NumberFormat, ,như là dấu tách thập phân và .là dấu tách nhóm nghìn. Chỉ DecimalFormathoạt động.
WesternGun

Tại sao không phải tất cả các nước đều có sẵn ở đó? Tôi cảm thấy kỳ lạ về việc sử dụng ngôn ngữ Pháp để định dạng số Ba Lan ...
Dòng

18

Như E-Riz chỉ ra, NumberFormat.parse (Chuỗi) phân tích "1,23abc" là 1,23. Để lấy toàn bộ đầu vào, chúng ta có thể sử dụng:

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
Chiến lược này được giải thích chi tiết ở đây: ibm.com/developerworks/library/j-numberformat
Janus Varmarken

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

... Rất nhanh khi nó tìm kiếm mảng ký tự cơ bản trên cơ sở char-by-char. Chuỗi phiên bản thay thế biên dịch một RegEx để đánh giá.

Về cơ bản thay thế (char, char) nhanh hơn khoảng 10 lần và vì bạn sẽ thực hiện những điều này trong mã cấp thấp, nên suy nghĩ về điều này là hợp lý. Trình tối ưu hóa điểm nóng sẽ không phát hiện ra ... Chắc chắn là không có trên hệ thống của tôi.


4

Nếu bạn không biết chính xác Địa điểm và chuỗi có thể có một nghìn dấu phân cách thì đây có thể là giải pháp cuối cùng:

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

Lưu ý: điều này sẽ vui vẻ phân tích các chuỗi như "R 1 52,43,2" thành "15243.2".


4

Đây là phương thức tĩnh tôi sử dụng trong mã của riêng mình:

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
Điều gì xảy ra nếu Địa điểm mặc định là một cái gì đó giống như tiếng Đức, trong đó dấu phẩy biểu thị một vị trí thập phân? Bạn có thể chuyển vào, ví dụ "1.000.000" sẽ không phân tích thành Địa điểm Đức và sau đó sẽ được thay thế bằng "1.000.000" không phải là một Double hợp lệ.
Eddie Curtis

Xin chào @jimmycar, tôi vừa cập nhật câu trả lời của mình để sử dụng phiên bản hiện tại của phương thức tĩnh. Tôi hy vọng điều này sẽ giải quyết vấn đề của bạn! Pete
Pete


0

Trong trường hợp bạn không biết miền địa phương của giá trị chuỗi nhận được và nó không nhất thiết phải là miền địa phương giống như miền địa phương mặc định hiện tại, bạn có thể sử dụng miền này:

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

}

Nó sẽ trả về gấp đôi bất kể miền địa phương của chuỗi là gì. Và cho dù có bao nhiêu dấu phẩy hoặc điểm. Vì vậy, việc truyền 1,000,000.54sẽ hoạt động vì 1.000.000,54vậy bạn sẽ không phải dựa vào ngôn ngữ mặc định để phân tích chuỗi nữa. Mã không được tối ưu hóa vì nó có thể được vì vậy mọi đề xuất đều được chào đón. Tôi đã cố gắng kiểm tra hầu hết các trường hợp để đảm bảo nó giải quyết được vấn đề nhưng tôi không chắc nó bao gồm tất cả. Nếu bạn tìm thấy một giá trị phá vỡ cho tôi biết.


-5

Điều này sẽ làm công việc:

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

6
Câu hỏi ban đầu cho biết "Có cách nào tốt hơn để phân tích" 1,234 "để nhận 1,234 hơn: p = p.replaceAll (", ",". ");" , nếu bạn nghĩ replacekhác biệt đáng kể với việc sử dụng replaceAll, vui lòng giải thích tại sao.
SuperBiasedMan
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.