[ACCEPTED]-C# Decimal.Parse issue with commas-decimal

Accepted answer
Score: 14

It's allowing thousands, because the default 5 NumberStyles value used by Decimal.Parse (NumberStyles.Number) includes NumberStyles.AllowThousands.

If you want 4 to disallow the thousands separators, you 3 can just remove that flag, like this:

Decimal.Parse("1,2,3,4", NumberStyles.Number ^ NumberStyles.AllowThousands)

(the 2 above code will throw an InvalidFormatException, which is what 1 you want, right?)

Score: 10

I ended up having to write the code to verify 9 the currency manually. Personally, for a 8 framework that prides itself for having 7 all the globalization stuff built in, it's 6 amazing .NET doesn't have anything to handle 5 this.

My solution is below. It works for 4 all the locales in the framework. It doesn't 3 support Negative numbers, as Orion pointed 2 out below, though. What do you guys think?

    public static bool TryParseCurrency(string value, out decimal result)
    {
        result = 0;
        const int maxCount = 100;
        if (String.IsNullOrEmpty(value))
            return false;

        const string decimalNumberPattern = @"^\-?[0-9]{{1,{4}}}(\{0}[0-9]{{{2}}})*(\{0}[0-9]{{{3}}})*(\{1}[0-9]+)*$";

        NumberFormatInfo format = CultureInfo.CurrentCulture.NumberFormat;

        int secondaryGroupSize = format.CurrencyGroupSizes.Length > 1
                ? format.CurrencyGroupSizes[1]
                : format.CurrencyGroupSizes[0];

        var r = new Regex(String.Format(decimalNumberPattern
                                       , format.CurrencyGroupSeparator==" " ? "s" : format.CurrencyGroupSeparator
                                       , format.CurrencyDecimalSeparator
                                       , secondaryGroupSize
                                       , format.CurrencyGroupSizes[0]
                                       , maxCount), RegexOptions.Compiled | RegexOptions.CultureInvariant);
        return !r.IsMatch(value.Trim()) ? false : Decimal.TryParse(value, NumberStyles.Any, CultureInfo.CurrentCulture, out result);
    }

And 1 here's one test to show it working (nUnit):

    [Test]
    public void TestCurrencyStrictParsingInAllLocales()
    {
        var originalCulture = CultureInfo.CurrentCulture;
        var cultures = CultureInfo.GetCultures(CultureTypes.SpecificCultures);
        const decimal originalNumber = 12345678.98m;
        foreach(var culture in cultures)
        {
            var stringValue = originalNumber.ToCurrencyWithoutSymbolFormat();
            decimal resultNumber = 0;
            Assert.IsTrue(DecimalUtils.TryParseCurrency(stringValue, out resultNumber));
            Assert.AreEqual(originalNumber, resultNumber);
        }
        System.Threading.Thread.CurrentThread.CurrentCulture = originalCulture;

    }
Score: 1

You might be able to do this in a two-phase 4 process. First you could verify the thousands 3 separator using the information in the CultureInfo.CurrentCulture.NumberFormat.NumberGroupSeparator and 2 CultureInfo.CurrentCulture.NumberFormat.NumberGroupSizes throwing an exception if it doesn't pass 1 and then pass the number into the Decimal.Parse();

Score: 0

It is a common issue never solved by microsoft. So, I 13 don't understand why 1,2,3.00 (english culture 12 for example) is valid! You need to build 11 an algorith to examine group size and return 10 false/exception(like a failed double.parse) if 9 the test is not passed. I had a similar 8 problem in a mvc application, which build 7 in validator doesn't accept thousands..so 6 i've overwrite it with a custom, using double/decimal/float.parse, but 5 adding a logic to validate group size.

If 4 you want read my solution (it is used for 3 my mvc custom validator, but you can use 2 it to have a better double/decimal/float.parse 1 generic validator) go here https://stackoverflow.com/a/41916721/3930528

More Related questions