简体   繁体   English

C#Decimal.Parse问题用逗号

[英]C# Decimal.Parse issue with commas

Here's my problem (for en-US): 这是我的问题(对于en-US):

Decimal.Parse("1,2,3,4") returns 1234, instead of throwing an InvalidFormatException. Decimal.Parse("1,2,3,4")返回1234,而不是抛出InvalidFormatException。

Most Windows applications (Excel en-US) do not drop the thousand separators and do not consider that value a decimal number. 大多数Windows应用程序(Excel en-US)不会删除千位分隔符,也不会将该值视为十进制数。 The same issue happens for other languages (although with different characters). 其他语言也会出现同样的问题(尽管字符不同)。

Are there any other decimal parsing libraries out there that solve this issue? 还有其他十进制解析库可以解决这个问题吗?

Thanks! 谢谢!

It's allowing thousands, because the default NumberStyles value used by Decimal.Parse ( NumberStyles.Number ) includes NumberStyles.AllowThousands . 它允许数千,因为Decimal.ParseNumberStyles.Number )使用的默认NumberStyles值包括NumberStyles.AllowThousands

If you want to disallow the thousands separators, you can just remove that flag, like this: 如果你想禁止数千个分隔符,你可以删除那个标志,如下所示:

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

(the above code will throw an InvalidFormatException , which is what you want, right?) (上面的代码会抛出一个InvalidFormatException ,这就是你想要的,对吧?)

I ended up having to write the code to verify the currency manually. 我最终不得不编写代码来手动验证货币。 Personally, for a framework that prides itself for having all the globalization stuff built in, it's amazing .NET doesn't have anything to handle this. 就个人而言,对于一个以内置所有全球化内容而自豪的框架,令人惊讶的.NET没有任何东西可以解决这个问题。

My solution is below. 我的解决方案如下。 It works for all the locales in the framework. 它适用于框架中的所有语言环境。 It doesn't support Negative numbers, as Orion pointed out below, though. 它不支持负数,正如Orion在下面指出的那样。 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 here's one test to show it working (nUnit): 这是一个显示它工作的测试(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;

    }

You might be able to do this in a two-phase process. 您可以在两个阶段中完成此操作。 First you could verify the thousands separator using the information in the CultureInfo.CurrentCulture.NumberFormat.NumberGroupSeparator and CultureInfo.CurrentCulture.NumberFormat.NumberGroupSizes throwing an exception if it doesn't pass and then pass the number into the Decimal.Parse() ; 首先,您可以使用CultureInfo.CurrentCulture.NumberFormat.NumberGroupSeparatorCultureInfo.CurrentCulture.NumberFormat.NumberGroupSizes的信息验证千位分隔符,如果它未通过则抛出异常,然后将该数字传递给Decimal.Parse() ;

It is a common issue never solved by microsoft. 这是微软从未解决过的常见问题。 So, I don't understand why 1,2,3.00 (english culture for example) is valid! 所以,我不明白为什么1,2,3.00(例如英国文化)是有效的! You need to build an algorith to examine group size and return false/exception(like a failed double.parse) if the test is not passed. 如果测试未通过,您需要构建一个算法来检查组大小并返回false / exception(如失败的double.parse)。 I had a similar problem in a mvc application, which build in validator doesn't accept thousands..so i've overwrite it with a custom, using double/decimal/float.parse, but adding a logic to validate group size. 我在mvc应用程序中遇到类似的问题,在验证器中构建不接受数千个。所以我用自定义覆盖它,使用double / decimal / float.parse,但添加逻辑来验证组大小。

If you want read my solution (it is used for my mvc custom validator, but you can use it to have a better double/decimal/float.parse generic validator) go here https://stackoverflow.com/a/41916721/3930528 如果你想读我的解决方案(它用于我的mvc自定义验证器,但你可以使用它有一个更好的双/十进制/ float.parse通用验证器)去这里https://stackoverflow.com/a/41916721/3930528

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM