소수점이있는 문자열을 두 배로 구문 분석하는 방법은 무엇입니까?


231

"3.5"더블 과 같은 문자열을 구문 분석하고 싶습니다 . 하나,

double.Parse("3.5") 

수율 35와

double.Parse("3.5", System.Globalization.NumberStyles.AllowDecimalPoint) 

을 던졌습니다 FormatException.

이제 내 컴퓨터의 로케일이 독일어로 설정되어 있으며 쉼표는 소수점 구분 기호로 사용됩니다. 그것은 그것으로 뭔가를해야 할 수도 있고 입력으로 double.Parse()기대 "3,5"하지만 확실하지 않습니다.

현재 로케일에 지정된대로 형식화되거나 형식화되지 않은 10 진수를 포함하는 문자열을 구문 분석하려면 어떻게해야합니까?


십진 쉼표는 확실히 출력에 영향을 미칩니다.
ChrisF

12
상황에 맞는 double.TryParse () 메소드를 잊지 마십시오.
Kyle Gagnet

답변:


414
double.Parse("3.5", CultureInfo.InvariantCulture)

나는 XmlConvert수업 을 사용하고 싶습니다 ... 이것이 사용하는 것보다 낫거나, 나쁘거나, 또는 다른지에 대한 아이디어가 CultureInfo.InvariantCulture있습니까?
ChrisW

1
글쎄, XmlConvert실제로 코드에서 단일 double 값을 구문 분석하는 데 사용되지는 않습니다. 나는 내 의도를 분명하게 사용 double.Parse하거나 사용하는 것을 선호합니다 Convert.ToDouble.
Mehrdad Afshari

4
이것은 doulble.Parse가 점을 소수점으로 포함하지 않을 수있는 기본 문화권을 사용합니까?
Ahmed 님이

3
12345678.12345678을 변환하면 너무 변환합니다. 12345678.123457
PUG

4
나를 위해 작동하지 않았다 : 쉼표를 건너 뛰고 int를 double로 두 번
fnc12

75

나는 일반적으로 다중 문화권 함수를 사용하여 사용자 입력을 구문 분석합니다. 주로 누군가가 숫자 키패드에 익숙하고 쉼표를 소수점 구분 기호로 사용하는 문화권을 사용하는 경우 해당 사람은 쉼표 대신 숫자 패드의 포인트를 사용하기 때문입니다.

public static double GetDouble(string value, double defaultValue)
{
    double result;

    //Try parsing in the current culture
    if (!double.TryParse(value, System.Globalization.NumberStyles.Any, CultureInfo.CurrentCulture, out result) &&
        //Then try in US english
        !double.TryParse(value, System.Globalization.NumberStyles.Any, CultureInfo.GetCultureInfo("en-US"), out result) &&
        //Then in neutral language
        !double.TryParse(value, System.Globalization.NumberStyles.Any, CultureInfo.InvariantCulture, out result))
    {
        result = defaultValue;
    }

    return result;
}

그러나 @nikie 의견은 사실입니다. 방어를 위해 문화가 en-US, en-CA 또는 fr-CA 일 수 있다는 것을 알고있는 통제 된 환경에서이 기능을 사용합니다. 프랑스어에서는 쉼표를 소수점 구분 기호로 사용하기 때문에이 기능을 사용하지만 재무 분야에서 일한 사람은 항상 숫자 키패드에서 소수점 구분 기호를 사용하지만이 점은 쉼표가 아닌 점입니다. 따라서 fr-CA 문화에서도 소수점 구분 기호가 될 숫자를 구문 분석해야합니다.


18
좋은 생각인지 잘 모르겠습니다. 문화를 모르면 이중 값을 확실하게 구문 분석 할 수 없습니다. 독일에서는 이중 값에 '.'가 포함될 수 있지만 거기에는 수천 구분 기호로 간주됩니다. 따라서 Legate의 경우 GetDouble ( "3.5")은 독일어 로캘에서 35를 반환하고 en-us 환경에서 3.5를 반환합니다.
Niki

아니요, Pierre Alain은 정확하게 쓰여진대로 정확합니다. 문화에 "점은 천 단위"구분 기호가 있으면 "3.5"는 "35"로 표시되며 좋습니다. 그러나 "도트"에 대한 규칙이없는 것으로 배양하면 문자가 소수점으로 구문 분석되어 작동합니다. 나는 enUS 문화를 시험해 보지 않았지만 개인적인 선택입니다.
xryl669

쉼표와 함께 문화권에서 숫자 키패드를 사용하면 도트 키가 쉼표로 설정됩니다.
CrazyBaran

숫자 키패드 소수점 구분 기호는 키보드 레이아웃 (및 지역 설정이 아닌 Windows 7 이상)에 따라 다릅니다. (헝가리어를 사용하여 텍스트, 전자 메일을 작성하고 en-US를 사용하여 코드를 작성하므로 점이 될 수 있습니다. 또한 쉼표를 사용하여 소수 구분 기호를 ','에서 '.'로 변경하고 목록 구분 기호를 ';'에서 ','로 변경 한 사용자 지정 지역 설정을 사용합니다. -culture apps;)
Steven Spark

25

나는 의견을 쓸 수 없으므로 여기에 씁니다.

double.Parse ( "3.5", CultureInfo.InvariantCulture) 는 좋은 생각이 아닙니다. 캐나다에서는 3.5 대신 3,5를 쓰고이 함수는 결과적으로 35를줍니다.

내 컴퓨터에서 두 가지를 모두 테스트했습니다.

double.Parse("3.5", CultureInfo.InvariantCulture) --> 3.5 OK
double.Parse("3,5", CultureInfo.InvariantCulture) --> 35 not OK

이것이 Pierre-Alain Vigeant가 언급 한 올바른 방법입니다

public static double GetDouble(string value, double defaultValue)
{
    double result;

    // Try parsing in the current culture
    if (!double.TryParse(value, System.Globalization.NumberStyles.Any, CultureInfo.CurrentCulture, out result) &&
        // Then try in US english
        !double.TryParse(value, System.Globalization.NumberStyles.Any, CultureInfo.GetCultureInfo("en-US"), out result) &&
        // Then in neutral language
        !double.TryParse(value, System.Globalization.NumberStyles.Any, CultureInfo.InvariantCulture, out result))
    {
        result = defaultValue;
    }
    return result;
}

1
Re : "... 캐나다에서는 3.5 대신 3,5를 씁니다." 확실합니까? 에 따르면 진수 표시 :. "점은 나라" "진수 표시로 사용이 포함 ... 캐나다 (영어를 사용하는 경우)" . 프랑스어 버전의 Windows를 사용하는 것이 더 중요하지 않습니까?
피터 Mortensen

프랑스어 버전 때문에 Baybe. 몬트리올에서는 3.5가 아닌 3,5를 씁니다
Malus Jan

그래서 당신의 논리에 따르면, 항상 그들 중 하나만 true를 반환합니까?
batmaci


여전히 오류가 있습니다. GetDouble ( "10 ,,,,,,,, 0", 0.0)과 같은 입력 문자열의 경우. 언급 된 함수는 100을 반환합니다.
Krivers

21
Double.Parse("3,5".Replace(',', '.'), CultureInfo.InvariantCulture)

구문 분석하기 전에 쉼표를 점으로 바꾸십시오. 소수점 구분 기호로 쉼표가있는 국가에서 유용합니다. 사용자 입력 (필요한 경우)을 하나의 쉼표 나 점으로 제한하는 것을 고려하십시오.


1
+133 표의 답변보다 훨씬 더 정확한 답변 ... ","또는 "."가있는 두 시스템 모두에서 살 수 있습니다. 소수 구분 기호 ...
Badiboy

@Badiboy이 답변에 무엇이 잘못되었는지 설명 할 수 있습니까? 내가 알기로 InvariantCulture는 항상 '.' 소수점 구분 기호로. 따라서 두 시스템 모두에서 작동합니다.
Alex P.

@ Alex11223 당신이 맞아요. 그렇기 때문에이 답변이 더 인기있는 답변보다 낫다고 말했습니다. 추신 : LIST SEPARATOR (예 : 1,234,560.01)와 같이 ","가 있으면이 코드를 친절하게 말하면 실패하지만 어떻게 해결 해야할지 모르겠습니다. :)
Badiboy 2016 년

4
일부 cultureinfos에서, 천 단위 구분 기호를 사용할 수 있기 때문에 이것은 좋은 대답이 아닙니다. 점으로 바꾸면 여러 점이 생겨 구문 분석이 실패합니다. Double.Parse ((12345.67) .ToString ( "N", new CultureInfo ( "en")). Replace ( ',', '. '), CultureInfo.InvariantCulture) 때문에 (12345.67) .ToString ( "N", new CultureInfo ( "en")). Replace (', ','. ') 형식은 "12.345.67"
codingdave

1,234.56-> 1.234.56 구문 분석기가 아닙니다. 또 다른 아이디어는 숫자에 '.'이 포함되어 있는지 확인하는 것입니다. ','및 ','을 빈 문자열로 바꾸고 ','만 쉼표로 표시하면 '.'로 바꿉니다.
GDocal

16

비결은 문화를 사용하여 모든 문화권에서 점을 파싱하는 것입니다.

double.Parse("3.5", System.Globalization.NumberStyles.AllowDecimalPoint, System.Globalization.NumberFormatInfo.InvariantInfo);

11

상수 문자열로 문자열 대체를 제안하는 위의 모든 대답은 잘못 될 수 있습니다. 왜? Windows의 지역 설정을 존중하지 않기 때문에! Windows는 사용자가 원하는 구분 문자를 자유롭게 설정할 수 있도록합니다. S / He는 제어판을 열고 지역 패널로 이동하여 고급을 클릭하고 언제든지 캐릭터를 변경할 수 있습니다. 프로그램 실행 중에도. 이것을 생각하십시오. 좋은 해결책은 이것을 알고 있어야합니다.

따라서 먼저이 숫자의 출처를 파싱 할 것인지 스스로에게 물어봐야합니다. .NET Framework의 입력에서 오는 경우 동일한 형식이므로 문제가 없습니다. 그러나 외부에서, 외부 서버에서, 아마도 문자열 속성 만 지원하는 오래된 DB에서 온 것일 수 있습니다. 여기서 db 관리자는 숫자를 저장하는 형식의 규칙을 제공해야합니다. 예를 들어 미국 형식의 미국 DB가 될 것임을 알고 있다면 다음 코드를 사용할 수 있습니다.

CultureInfo usCulture = new CultureInfo("en-US");
NumberFormatInfo dbNumberFormat = usCulture.NumberFormat;
decimal number = decimal.Parse(db.numberString, dbNumberFormat);

이것은 세계 어느 곳에서나 잘 작동합니다. 그리고 'Convert.ToXxxx'를 사용하지 마십시오. 'Convert'클래스는 모든 방향의 전환을위한 기반으로 만 생각됩니다. 게다가 : DateTimes에도 비슷한 메커니즘을 사용할 수 있습니다.


동의했다! Culture 기능을 수동으로 구현하려고하면 결국 예상치 못한 상황이 발생하고 큰 두통이 발생합니다. .NET이 올바르게 처리하도록하십시오.
Khalos

2
큰 문제는 사용자가 문화 설정을 위해 소수점 구분 기호로 간주되지 않는 소수점 구분 기호를 사용하는 경우입니다.
EdmundYeung99

3
string testString1 = "2,457";
string testString2 = "2.457";    
double testNum = 0.5;
char decimalSepparator;
decimalSepparator = testNum.ToString()[1];

Console.WriteLine(double.Parse(testString1.Replace('.', decimalSepparator).Replace(',', decimalSepparator)));
Console.WriteLine(double.Parse(testString2.Replace('.', decimalSepparator).Replace(',', decimalSepparator)));

2

이 주제에 대한 나의 두 센트는 일반적인 이중 변환 방법을 제공하려고합니다.

private static double ParseDouble(object value)
{
    double result;

    string doubleAsString = value.ToString();
    IEnumerable<char> doubleAsCharList = doubleAsString.ToList();

    if (doubleAsCharList.Where(ch => ch == '.' || ch == ',').Count() <= 1)
    {
        double.TryParse(doubleAsString.Replace(',', '.'),
            System.Globalization.NumberStyles.Any,
            CultureInfo.InvariantCulture,
            out result);
    }
    else
    {
        if (doubleAsCharList.Where(ch => ch == '.').Count() <= 1
            && doubleAsCharList.Where(ch => ch == ',').Count() > 1)
        {
            double.TryParse(doubleAsString.Replace(",", string.Empty),
                System.Globalization.NumberStyles.Any,
                CultureInfo.InvariantCulture,
                out result);
        }
        else if (doubleAsCharList.Where(ch => ch == ',').Count() <= 1
            && doubleAsCharList.Where(ch => ch == '.').Count() > 1)
        {
            double.TryParse(doubleAsString.Replace(".", string.Empty).Replace(',', '.'),
                System.Globalization.NumberStyles.Any,
                CultureInfo.InvariantCulture,
                out result);
        }
        else
        {
            throw new ParsingException($"Error parsing {doubleAsString} as double, try removing thousand separators (if any)");
        }
    }

    return result;
}

예상대로 작동합니다 :

  • 1.1
  • 1,1
  • 1,000,000,000
  • 1.000.000.000
  • 1,000,000,000.99
  • 1.000.000.000,99
  • 5,000,111.3
  • 5.000.111,3
  • 0.99,000,111,88
  • 0,99.000.111.88

없음 기본 변환은 구문 분석을 시도 실패, 그래서 실행되지 않습니다 1.3,14, 1,3.14또는 유사한 경우.


1
천으로 의도 된 "1,000"은 실패합니다.
Defkon1 2009 년

1

다음 코드는 모든 시나리오에서 작업을 수행합니다. 약간 파싱입니다.

List<string> inputs = new List<string>()
{
    "1.234.567,89",
    "1 234 567,89",
    "1 234 567.89",
    "1,234,567.89",
    "123456789",
    "1234567,89",
    "1234567.89",
};
string output;

foreach (string input in inputs)
{
    // Unify string (no spaces, only .)
    output = input.Trim().Replace(" ", "").Replace(",", ".");

    // Split it on points
    string[] split = output.Split('.');

    if (split.Count() > 1)
    {
        // Take all parts except last
        output = string.Join("", split.Take(split.Count()-1).ToArray());

        // Combine token parts with last part
        output = string.Format("{0}.{1}", output, split.Last());
    }

    // Parse double invariant
    double d = double.Parse(output, CultureInfo.InvariantCulture);
    Console.WriteLine(d);
}

2
1.234.567.890은 1234567.890을 반환합니다
Dan Vogel

나는 시도하지 않은,하지만 당신은 다른 문화 SO의에서 응용 프로그램을 실행하면,이 코드는 트릭을하지 것이라고 생각 : /
다니 주교

1

값이 사용자 입력에서 나온 경우 100 % 올바른 변환이 불가능하다고 생각합니다. 예를 들어 값이 123.456 인 경우 그룹화하거나 소수점이 될 수 있습니다. 실제로 100 %가 필요한 경우 형식을 설명하고 올바르지 않은 경우 예외를 발생시켜야합니다.

그러나 JanW의 코드를 개선하여 100 %에 대해 조금 더 앞서 나가고 있습니다. 아이디어는 마지막 구분자가 groupSeperator 인 경우 double보다 정수 유형이된다는 것입니다.

이러한 코드는 제 인 경우GetDouble .

void Main()
{
    List<string> inputs = new List<string>() {
        "1.234.567,89",
        "1 234 567,89",
        "1 234 567.89",
        "1,234,567.89",
        "1234567,89",
        "1234567.89",
        "123456789",
        "123.456.789",
        "123,456,789,"
    };

    foreach(string input in inputs) {
        Console.WriteLine(GetDouble(input,0d));
    }

}

public static double GetDouble(string value, double defaultValue) {
    double result;
    string output;

    // Check if last seperator==groupSeperator
    string groupSep = System.Globalization.CultureInfo.CurrentCulture.NumberFormat.NumberGroupSeparator;
    if (value.LastIndexOf(groupSep) + 4 == value.Count())
    {
        bool tryParse = double.TryParse(value, System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.CurrentCulture, out result);
        result = tryParse ? result : defaultValue;
    }
    else
    {
        // Unify string (no spaces, only . )
        output = value.Trim().Replace(" ", string.Empty).Replace(",", ".");

        // Split it on points
        string[] split = output.Split('.');

        if (split.Count() > 1)
        {
            // Take all parts except last
            output = string.Join(string.Empty, split.Take(split.Count()-1).ToArray());

            // Combine token parts with last part
            output = string.Format("{0}.{1}", output, split.Last());
        }
        // Parse double invariant
        result = double.Parse(output, System.Globalization.CultureInfo.InvariantCulture);
    }
    return result;
}

1
        var doublePattern = @"(?<integer>[0-9]+)(?:\,|\.)(?<fraction>[0-9]+)";
        var sourceDoubleString = "03444,44426";
        var match = Regex.Match(sourceDoubleString, doublePattern);

        var doubleResult = match.Success ? double.Parse(match.Groups["integer"].Value) + (match.Groups["fraction"].Value == null ? 0 : double.Parse(match.Groups["fraction"].Value) / Math.Pow(10, match.Groups["fraction"].Value.Length)): 0;
        Console.WriteLine("Double of string '{0}' is {1}", sourceDoubleString, doubleResult);

0

모든 구문 분석에서 로캘을 지정하는 대신 응용 프로그램 전체 로캘을 설정하는 것이 좋습니다.하지만 문자열 형식이 응용 프로그램에서 일관성이 없으면 작동하지 않을 수 있습니다.

CultureInfo.DefaultThreadCurrentCulture = new CultureInfo("pt-PT");
CultureInfo.DefaultThreadCurrentUICulture = new CultureInfo("pt-PT");

응용 프로그램을 시작할 때 이것을 정의하면 모든 이중 구문 분석에서 쉼표가 10 진수 구분 기호로 예상됩니다. 소수 및 천 단위 구분 기호가 구문 분석 할 문자열에 맞도록 적절한 로케일을 설정할 수 있습니다.


0

찾을 소수점 구분 기호를 지정하지 않으면 어렵지만 그렇게하면 내가 사용하는 것입니다.

    public static double Parse(string str, char decimalSep)
    {
        string s = GetInvariantParseString(str, decimalSep);
        return double.Parse(s, System.Globalization.CultureInfo.InvariantCulture);
    }

    public static bool TryParse(string str, char decimalSep, out double result)
    {
        // NumberStyles.Float | NumberStyles.AllowThousands got from Reflector
        return double.TryParse(GetInvariantParseString(str, decimalSep), NumberStyles.Float | NumberStyles.AllowThousands, System.Globalization.CultureInfo.InvariantCulture, out result);
    }

    private static string GetInvariantParseString(string str, char decimalSep)
    {
        str = str.Replace(" ", "");

        if (decimalSep != '.')
            str = SwapChar(str, decimalSep, '.');

        return str;
    }
    public static string SwapChar(string value, char from, char to)
    {
        if (value == null)
            throw new ArgumentNullException("value");

        StringBuilder builder = new StringBuilder();

        foreach (var item in value)
        {
            char c = item;
            if (c == from)
                c = to;
            else if (c == to)
                c = from;

            builder.Append(c);
        }
        return builder.ToString();
    }

    private static void ParseTestErr(string p, char p_2)
    {
        double res;
        bool b = TryParse(p, p_2, out res);
        if (b)
            throw new Exception();
    }

    private static void ParseTest(double p, string p_2, char p_3)
    {
        double d = Parse(p_2, p_3);
        if (d != p)
            throw new Exception();
    }

    static void Main(string[] args)
    {
        ParseTest(100100100.100, "100.100.100,100", ',');
        ParseTest(100100100.100, "100,100,100.100", '.');
        ParseTest(100100100100, "100.100.100.100", ',');
        ParseTest(100100100100, "100,100,100,100", '.');
        ParseTestErr("100,100,100,100", ',');
        ParseTestErr("100.100.100.100", '.');
        ParseTest(100100100100, "100 100 100 100.0", '.');
        ParseTest(100100100.100, "100 100 100.100", '.');
        ParseTest(100100100.100, "100 100 100,100", ',');
        ParseTest(100100100100, "100 100 100,100", '.');
        ParseTest(1234567.89, "1.234.567,89", ',');    
        ParseTest(1234567.89, "1 234 567,89", ',');    
        ParseTest(1234567.89, "1 234 567.89",     '.');
        ParseTest(1234567.89, "1,234,567.89",    '.');
        ParseTest(1234567.89, "1234567,89",     ',');
        ParseTest(1234567.89, "1234567.89",  '.');
        ParseTest(123456789, "123456789", '.');
        ParseTest(123456789, "123456789", ',');
        ParseTest(123456789, "123.456.789", ',');
        ParseTest(1234567890, "1.234.567.890", ',');
    }

이것은 모든 문화와 함께 작동해야합니다. 스왑 대신 바꾸는 구현과 달리 소수점 구분 기호가 두 개 이상인 문자열을 올바르게 구문 분석하지 못합니다.


0

@JanW의 코드도 개선했습니다 ...

의료 기기의 결과를 형식화해야하며 "> 1000", "23.3e02", "350E-02"및 "NEGATIVE"도 보냅니다.

private string FormatResult(string vResult)
{
  string output;
  string input = vResult;

  // Unify string (no spaces, only .)
  output = input.Trim().Replace(" ", "").Replace(",", ".");

  // Split it on points
  string[] split = output.Split('.');

  if (split.Count() > 1)
  {
    // Take all parts except last
    output = string.Join("", split.Take(split.Count() - 1).ToArray());

    // Combine token parts with last part
    output = string.Format("{0}.{1}", output, split.Last());
  }
  string sfirst = output.Substring(0, 1);

  try
  {
    if (sfirst == "<" || sfirst == ">")
    {
      output = output.Replace(sfirst, "");
      double res = Double.Parse(output);
      return String.Format("{1}{0:0.####}", res, sfirst);
    }
    else
    {
      double res = Double.Parse(output);
      return String.Format("{0:0.####}", res);
    }
  }
  catch
  {
    return output;
  }
}

-2
System.Globalization.CultureInfo ci = System.Globalization.CultureInfo.CurrentCulture;

string _pos = dblstr.Replace(".",
    ci.NumberFormat.NumberDecimalSeparator).Replace(",",
        ci.NumberFormat.NumberDecimalSeparator);

double _dbl = double.Parse(_pos);

-3

나는 이것이 가장 좋은 대답이라고 생각합니다.

public static double StringToDouble(string toDouble)
{
    toDouble = toDouble.Replace(",", "."); //Replace every comma with dot

    //Count dots in toDouble, and if there is more than one dot, throw an exception.
    //Value such as "123.123.123" can't be converted to double
    int dotCount = 0;
    foreach (char c in toDouble) if (c == '.') dotCount++; //Increments dotCount for each dot in toDouble
    if (dotCount > 1) throw new Exception(); //If in toDouble is more than one dot, it means that toCount is not a double

    string left = toDouble.Split('.')[0]; //Everything before the dot
    string right = toDouble.Split('.')[1]; //Everything after the dot

    int iLeft = int.Parse(left); //Convert strings to ints
    int iRight = int.Parse(right);

    //We must use Math.Pow() instead of ^
    double d = iLeft + (iRight * Math.Pow(10, -(right.Length)));
    return d;
}

코드를 설명하고 자세한 정보를 제공하면 도움이 될 것입니다.
Charlie Fish

여기서 무엇을 설명해야합니까? 모든 것이 코멘트입니다
Endorphinex

-3

아래는 덜 효율적이지만이 논리를 사용합니다. 소수점 이하 두 자리가있는 경우에만 유효합니다.

double val;

if (temp.Text.Split('.').Length > 1)
{
    val = double.Parse(temp.Text.Split('.')[0]);

    if (temp.Text.Split('.')[1].Length == 1)
        val += (0.1 * double.Parse(temp.Text.Split('.')[1]));
    else
        val += (0.01 * double.Parse(temp.Text.Split('.')[1]));
}
else
    val = double.Parse(RR(temp.Text));

-5

숫자를 곱한 다음 이전에 곱한 값으로 나눕니다.

예를 들어

perc = double.Parse("3.555)*1000;
result = perc/1000
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.