Parse strings to double with comma and point

Viewed 214449

I am trying to write a function which basically converts an array of strings to an array of strings where all the doubles in the array are rounded to the number of decimalplaces i set. There can also be strings in the array which are no double values at all.

string[,] values = new string[1, 3];

values[0, 0] = "hello";
values[0, 1] = "0.123";
values[0, 2] = "0,123";

int decimalPlaces = 2;

double tmp;
string format = "F" + decimalPlaces.ToString();
IFormatProvider provider = CultureInfo.InvariantCulture;
for (int i = 0; i < values.GetLength(0); i++)
{
    for (int j = 0; j < values.GetLength(1); j++)
    {
        if (double.TryParse(values[i, j], out tmp))
        {
            values[i, j] = tmp.ToString(format, provider);
        }
    }
}

Console.ReadLine();

The result has to be: "hello" , "0.12", "0.12" but it is "hello", "123.00", "0.12" will treat the comma in the wrong way. Does anyone have a simple and efficient solution for this?

14 Answers

Make two static cultures, one for comma and one for point.

    var commaCulture = new CultureInfo("en")
    {
        NumberFormat =
        {
            NumberDecimalSeparator = ","
        }
    };

    var pointCulture = new CultureInfo("en")
    {
        NumberFormat =
        {
            NumberDecimalSeparator = "."
        }
    };

Then use each one respectively, depending on the input (using a function):

    public double ConvertToDouble(string input)
    {
        input = input.Trim();

        if (input == "0") {
            return 0;
        }

        if (input.Contains(",") && input.Split(',').Length == 2)
        {
            return Convert.ToDouble(input, commaCulture);
        }

        if (input.Contains(".") && input.Split('.').Length == 2)
        {
            return Convert.ToDouble(input, pointCulture);
        }

        throw new Exception("Invalid input!");
    }

Then loop through your arrays

    var strings = new List<string> {"0,12", "0.122", "1,23", "00,0", "0.00", "12.5000", "0.002", "0,001"};
    var doubles = new List<double>();

    foreach (var value in strings) {
        doubles.Add(ConvertToDouble(value));
    }

This should work even though the host environment and culture changes.

Simple use:

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

For me using the culture info was not an option, since the app was running on systems with different cultures but the input strings to be parsed where of unknown culture.

So I referred to normalizing the string representation and then converting it using CultureInfo.InvariantCulture:

    private static double NormalizeAndParse(string strDouble)
    {
        string strDoubleNormalized;

        if (strDouble.Contains(","))
        {
            var strReplaced = strDouble.Replace(",", ".");
            var decimalSeparatorPos = strReplaced.LastIndexOf('.');
            var strInteger = strReplaced.Substring(0, decimalSeparatorPos);
            var strFractional = strReplaced.Substring(decimalSeparatorPos);

            strInteger = strInteger.Replace(".", string.Empty);
            strDoubleNormalized = strInteger + strFractional;
        }
        else
        {
            strDoubleNormalized = strDouble;
        }

        return Double.Parse(strDoubleNormalized, NumberStyles.Any, CultureInfo.InvariantCulture);
    }

Another option would be to use the information on the decimal separator from CultureInfo class. Knowing that we can replace ',' with '.' or vice versa when needed. A number grouping symbol is also available in this class if something needs to be done with a number like 1,000,000.23.

string decimalSeparator = CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator;
        string value = "";

        if (decimalSeparator == ".")
        {
            value = rateLimitTextBox.Text.Replace(",", ".");
        }
        else if (decimalSeparator == ",")
        {
            value = rateLimitTextBox.Text.Replace(".", ",");
        }

        bool LimitAcceptable = decimal.TryParse(value, NumberStyles.Any, CultureInfo.CurrentCulture, out decimal limit);

I use the following snippet if I want to parse a number with a decimal separator and nothing else:

public bool TryParseDecimal(string value, out decimal result) {
    const string your_separator = ",";

    var numberFormat = new NumberFormatInfo {
            NumberDecimalSeparator = your_separator
    };

    return decimal.TryParse(value, NumberStyles.AllowDecimalPoint, numberFormat, out result);
}

I don't think that using a culture or string manipulation expresses the intention of convertig a number with a non '.' decimal point.

First of all, I would like to thank Sinipelto.

Here is just a small improvements of his answer

First I decide to declare cultural as static, and took in consideration case when you have value without comma or dot. Hope this saves few seconds for someone

        public static CultureInfo pointCulture = new CultureInfo("en")
    {
        NumberFormat =
            {
                NumberDecimalSeparator = "."
            }
    };

    public static CultureInfo commaCulture = new System.Globalization.CultureInfo("en")
    {
        NumberFormat =
            {
                NumberDecimalSeparator = ","
            }
    };

    private double ConvertToDouble(string input)
    {
        input = input.Trim();

        if (!input.Contains(",") && !input.Contains(".")) 
        {
            return double.Parse(input);
        }

        if (input.Contains(",") && input.Split(',').Length == 2)
        {
            return Convert.ToDouble(input, commaCulture);
        }

        if (input.Contains(".") && input.Split('.').Length == 2)
        {
            return Convert.ToDouble(input, pointCulture);
        }

        throw new Exception("Invalid input!");
    }
Related