C # Decimal.Parse проблема с запятыми - PullRequest
21 голосов
/ 07 мая 2009

Вот моя проблема (для en-US):

Decimal.Parse("1,2,3,4") возвращает 1234 вместо исключения InvalidFormatException.

Большинство приложений Windows (Excel en-US) не удаляют разделители тысяч и не считают это значение десятичным числом. Та же проблема возникает для других языков (хотя и с другими символами).

Существуют ли другие библиотеки для анализа десятичных чисел, которые решают эту проблему?

Спасибо!

Ответы [ 4 ]

14 голосов
/ 07 мая 2009

Это разрешает тысячи, потому что значение NumberStyles по умолчанию, используемое Decimal.Parse (NumberStyles.Number), включает NumberStyles.AllowThousands.

Если вы хотите запретить разделители тысяч, вы можете просто удалить этот флаг, например так:

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

(приведенный выше код выдаст InvalidFormatException, чего вы хотите, верно?)

10 голосов
/ 08 мая 2009

Мне пришлось написать код, чтобы проверить валюту вручную. Лично для фреймворка, который гордится тем, что в него встроены все средства глобализации, удивительно .NET не имеет ничего, чтобы справиться с этим.

Мое решение ниже. Это работает для всех локалей в рамках. Он не поддерживает отрицательные числа, как указал Орион ниже. Что вы, ребята, думаете?

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

И вот один тест, чтобы показать, что он работает (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;

    }
1 голос
/ 07 мая 2009

Вы можете сделать это в два этапа. Сначала вы можете проверить разделитель тысяч, используя информацию в CultureInfo.CurrentCulture.NumberFormat.NumberGroupSeparator и CultureInfo.CurrentCulture.NumberFormat.NumberGroupSizes, вызывая исключение, если оно не проходит, а затем передать число в Decimal.Parse();

0 голосов
/ 29 января 2017

Это распространенная проблема, никогда не решаемая Microsoft. Итак, я не понимаю, почему 1,2,3.00 (например, английская культура) является действительным! Вам необходимо создать алгоритм для проверки размера группы и возврата false / исключения (например, неудачного double.parse), если тест не пройден. У меня была похожая проблема в приложении MVC, в котором встроенный валидатор не принимает тысячи ... поэтому я переписал его с помощью пользовательского, используя double / decimal / float.parse, но добавив логику для проверки размера группы. 1001 *

Если вы хотите прочитать мое решение (оно используется для моего пользовательского валидатора mvc, но вы можете использовать его, чтобы иметь лучший универсальный валидатор double / decimal / float.parse), перейдите сюда https://stackoverflow.com/a/41916721/3930528

...