user1104916
user1104916

Reputation: 113

parse decimal from string in any format

Any implementation for the next method:

public static decimal StringToDecimal(string sValue)
{
    if (string.IsNullOrEmpty(sValue))
    {
        return 0;
    }
    ...
}

my app imports data from excel or dbf files from other machines, I have no problem reading string data, but I didn't succeded to read properly numeric data especially Price column. The source machine may use comma as decimal separator or any formatting.

decimal.Parse(...) or decimal.TryParse(...) works only if the string format of the numeric value matches the app machine settings.

Upvotes: 3

Views: 1036

Answers (2)

user1104916
user1104916

Reputation: 113

This seems to work

    public static char DecimalSeparator(string sValue)
    {
        var decimalPosition = sValue.Length - 4;
        if (decimalPosition < 0)
        {
            return '.';
        }
        var decimalPart = sValue.Substring(decimalPosition);
        if (decimalPart.Contains(','))
        {
            return ',';
        }
        return '.';
    }

    public static decimal StringToDecimal(string toParse)
    {
        if (string.IsNullOrEmpty(toParse))
        {
            return 0;
        }

        var stb = new StringBuilder();
        var localDecimalSeparator = CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator.ToCharArray()[0];
        var sourceDecimalSeparator = DecimalSeparator(toParse);
        var sNumeric = "0123456789-" + sourceDecimalSeparator;

        for (var i = 0; i < toParse.Length; i++)
        {
            var currentChar = toParse[i];
            if (sNumeric.IndexOf(currentChar) > -1)
            {
                if (currentChar == sourceDecimalSeparator)
                {
                    currentChar = localDecimalSeparator;
                }
                stb.Append(currentChar);
            }
        }

        return decimal.Parse(stb.ToString());
    }

Upvotes: 0

Jon Skeet
Jon Skeet

Reputation: 1499860

When you call decimal.TryParse or decimal.Parse, you can specify a culture (and therefore avoid just picking up the system settings). You could try each culture you're interested in one at a time until you find one where the value parses successfully.

bool decimal TryParseAllCultures(string text, out value)
{
    foreach (var culture in cultures)
    {
        if (decimal.TryParse(text, NumberStyles.Number, culture, out value))
        {
            return true;
        }
    }
    return false;
}

(You need to decide which cultures to support, and whether NumberStyles.Number is actually the NumberStyle you're interested in.)

Having said that, it's pretty dangerous to do that. Consider "1,234". That could mean "one thousand and twenty-four" in a culture which uses the comma as a thousands separator, or it could mean "just less than one and a quarter" in a culture which uses the comma as a decimal separator. If you're not able to tell which you should use, you could easily go very wrong. If you know the expected range of values, you could use that as heuristic information to check which value you actually want, but it still feels a little worrying to me.

Upvotes: 4

Related Questions