쉼표로 소수점을 구분하는 가장 좋은 방법은?


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(",",".")는 단일 쉼표가있는 경우에만 작동한다는 것입니다. 즉, 1,234,567이 throw java.lang.NumberFormatException: multiple points됩니다. 긍정적 인 lookahead를 가진 정규 표현식으로 충분할 것입니다 p.replaceAll(",(?=[0-9]+,)", "").replaceAll(",", "."). regular-expressions.info/lookaround.html
artemisian

2
문제 없습니다. NumberFormatException이 좋습니다. 어떤 쉼표가 올바른지 어떻게 알 수 있습니까? 형식이 잘못되어 사용자에게 예외보다 읽기 쉬운 메시지를 표시하기 만하면됩니다.
놀라운 1

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
터키의 경우 NumberFormat.getInstance (new 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
또 다른 것은, 스페인어 로켈은 default` "로 표시되지되고 난을 구성 할 수 Locale와 올바른 형식 new Locale("es", "ES")과 자동으로 숫자 문자열을 구문 분석 NumberFormat과 함께, ,소수로 구분하고 .천 개 그룹 구분 기호로 만. DecimalFormat작동합니다.
WesternGun

모든 국가를 이용할 수없는 이유는 무엇입니까? 폴란드어 숫자를 형식화하기 위해 프랑스어 로케일을 사용하는 것이 이상하다고 생각합니다.
Line

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를 컴파일하여 평가합니다.

기본적으로 replace (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
기본 로케일이 독일어와 같은 경우 쉼표는 소수점 이하 자릿수를 나타냅니다. 예를 들어 "1,000,000"을 전달하면 독일어 로캘로 구문 분석되지 않으며 유효한 Double이 아닌 "1.000.000"으로 대체됩니다.
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);

}

문자열의 로케일에 관계없이 double을 리턴합니다. 그리고 얼마나 많은 쉼표 나 포인트가 있더라도. 따라서 전달 1,000,000.54하면 효과가 1.000.000,54있으므로 더 이상 문자열을 구문 분석하기 위해 기본 로케일에 의존 할 필요가 없습니다. 코드는 최적화 될 수 없으므로 어떤 제안도 환영합니다. 문제가 해결되는지 확인하기 위해 대부분의 사례를 테스트하려고 시도했지만 모든 문제를 다룰 수는 없습니다. 당신이 깨는 가치를 찾으면 알려주십시오.


-5

이것은 일을 할 것입니다 :

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

6
초기 질문에 "p.p.replaceAll (", ",". ");보다 1.234를 얻기 위해"1,234 "를 구문 분석하는 더 좋은 방법이 있습니까?" replace을 사용하는 것과 크게 다르다고 생각되면 replaceAll이유를 설명해주세요.
SuperBiasedMan
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.