IT story

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

hot-time 2020. 4. 29. 08:11
반응형

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


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

double.Parse("3.5") 

수율 35와

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

을 던졌습니다 FormatException.

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

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


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

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

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 문화에서도 소수점 구분 기호가 될 숫자를 구문 분석해야합니다.


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

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

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

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

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

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


상수 문자열로 문자열 대체를 제안하는 위의 모든 대답은 잘못 될 수 있습니다. 왜? 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에도 비슷한 메커니즘을 사용할 수 있습니다.


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

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

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

값이 사용자 입력에서 오는 경우 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;
}

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

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

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


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

    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", ',');
    }

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


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

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

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

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

No default conversion is implemented, so it would fail trying to parse 1.3,14, 1,3.14 or similar cases.


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

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

double _dbl = double.Parse(_pos);

I think it is the best answer:

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

The below is less efficient, but I use this logic. This is valid only if you have two digits after decimal point.

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

Multiply the number and then divide it by what you multiplied it by before.

For example,

perc = double.Parse("3.555)*1000;
result = perc/1000

참고URL : https://stackoverflow.com/questions/1354924/how-do-i-parse-a-string-with-a-decimal-point-to-a-double

반응형