Поделиться через


Анализ числовых строк в .NET

Все числовые типы имеют два метода статического синтаксического анализа, Parse и TryParseможно использовать для преобразования строкового представления числа в числовый тип. Эти методы позволяют анализировать строки, созданные с помощью строк форматирования, описанных в строках стандартного числового формата и настраиваемых строк числовых форматов. По умолчанию методы Parse и TryParse могут успешно преобразовывать строки, содержащие только десятичные цифры, в целые числовые значения. Они могут успешно преобразовать строки, содержащие целочисленные и дробные десятичные цифры, разделители групп и десятичный разделитель в значения с плавающей запятой. Метод Parse создает исключение, если операция завершается ошибкой, а TryParse метод возвращается false.

Примечание.

Начиная с .NET 7 числовые типы в .NET также реализуют System.IParsable<TSelf> интерфейс, определяющий IParsable<TSelf>.Parse и IParsable<TSelf>.TryParse методы.

Анализ и форматирование поставщиков

Как правило, строковые представления числовых значений отличаются в зависимости от культуры. Элементы числовых строк, такие как символы валют, разделители групп (или тысячи) и десятичные разделители, отличаются в зависимости от культурных особенностей. Методы синтаксического анализа неявно или явно используют поставщик формата, который распознает эти культуроспецифические вариации. Если поставщик формата не указан при вызове метода Parse или TryParse, используется поставщик формата, связанный с текущими языковыми и региональными параметрами (объект NumberFormatInfo, возвращаемый свойством NumberFormatInfo.CurrentInfo).

Поставщик формата представлен реализацией IFormatProvider . Этот интерфейс имеет одного члена, метод GetFormat, параметром которого является объект Type, представляющий тип для форматирования. Этот метод возвращает объект, предоставляющий сведения о форматировании. .NET поддерживает следующие две IFormatProvider реализации для синтаксического анализа числовых строк:

В следующем примере пытаются преобразовать каждую строку в массиве в значение Double. Сначала он пытается проанализировать строку, используя поставщика формата, который учитывает особенности культуры английского языка (Соединенные Штаты). Если эта операция вызывает исключение FormatException, она пытается проанализировать строку с помощью поставщика формата, который отражает культурные нормы французского языка (Франция).

using System;
using System.Globalization;

public class Example
{
   public static void Main()
   {
      string[] values = { "1,304.16", "$1,456.78", "1,094", "152",
                          "123,45 €", "1 304,16", "Ae9f" };
      double number;
      CultureInfo culture = null;

      foreach (string value in values) {
         try {
            culture = CultureInfo.CreateSpecificCulture("en-US");
            number = Double.Parse(value, culture);
            Console.WriteLine($"{culture.Name}: {value} --> {number}");
         }
         catch (FormatException) {
            Console.WriteLine($"{culture.Name}: Unable to parse '{value}'.");
            culture = CultureInfo.CreateSpecificCulture("fr-FR");
            try {
               number = Double.Parse(value, culture);
               Console.WriteLine($"{culture.Name}: {value} --> {number}");
            }
            catch (FormatException) {
               Console.WriteLine($"{culture.Name}: Unable to parse '{value}'.");
            }
         }
         Console.WriteLine();
      }
   }
}
// The example displays the following output:
//    en-US: 1,304.16 --> 1304.16
//
//    en-US: Unable to parse '$1,456.78'.
//    fr-FR: Unable to parse '$1,456.78'.
//
//    en-US: 1,094 --> 1094
//
//    en-US: 152 --> 152
//
//    en-US: Unable to parse '123,45 €'.
//    fr-FR: Unable to parse '123,45 €'.
//
//    en-US: Unable to parse '1 304,16'.
//    fr-FR: 1 304,16 --> 1304.16
//
//    en-US: Unable to parse 'Ae9f'.
//    fr-FR: Unable to parse 'Ae9f'.
Imports System.Globalization

Module Example
    Public Sub Main()
        Dim values() As String = {"1,304.16", "$1,456.78", "1,094", "152",
                                   "123,45 €", "1 304,16", "Ae9f"}
        Dim number As Double
        Dim culture As CultureInfo = Nothing

        For Each value As String In values
            Try
                culture = CultureInfo.CreateSpecificCulture("en-US")
                number = Double.Parse(value, culture)
                Console.WriteLine("{0}: {1} --> {2}", culture.Name, value, number)
            Catch e As FormatException
                Console.WriteLine("{0}: Unable to parse '{1}'.",
                                  culture.Name, value)
                culture = CultureInfo.CreateSpecificCulture("fr-FR")
                Try
                    number = Double.Parse(value, culture)
                    Console.WriteLine("{0}: {1} --> {2}", culture.Name, value, number)
                Catch ex As FormatException
                    Console.WriteLine("{0}: Unable to parse '{1}'.",
                                      culture.Name, value)
                End Try
            End Try
            Console.WriteLine()
        Next
    End Sub
End Module
' The example displays the following output:
'    en-US: 1,304.16 --> 1304.16
'    
'    en-US: Unable to parse '$1,456.78'.
'    fr-FR: Unable to parse '$1,456.78'.
'    
'    en-US: 1,094 --> 1094
'    
'    en-US: 152 --> 152
'    
'    en-US: Unable to parse '123,45 €'.
'    fr-FR: Unable to parse '123,45 €'.
'    
'    en-US: Unable to parse '1 304,16'.
'    fr-FR: 1 304,16 --> 1304.16
'    
'    en-US: Unable to parse 'Ae9f'.
'    fr-FR: Unable to parse 'Ae9f'.

Синтаксический анализ и значения NumberStyles

Элементы стиля (например, пробелы, разделители групп и десятичный разделитель), которые может обрабатывать операция синтаксического анализа, определяются значением NumberStyles перечисления. По умолчанию строки, представляющие целые значения, анализируются с помощью NumberStyles.Integer значения, которое разрешает только числовые цифры, начальные и конечные пробелы, а также начальный знак. Строки, представляющие значения с плавающей запятой, анализируются с помощью сочетания NumberStyles.Float значений и NumberStyles.AllowThousands значений. Этот составной стиль разрешает десятичные цифры вместе с начальным и конечным пробелом, ведущим знаком, десятичным разделителем, разделителем групп и экспонентом. Вызвав перегрузку метода Parse или TryParse, включающую параметр типа NumberStyles и установив один или несколько флагов NumberStyles, можно управлять стилевыми элементами, которые могут быть в строке, чтобы операция анализа успешно завершилась.

Например, строка, содержащая разделитель групп, не может быть преобразована в Int32 значение с помощью Int32.Parse(String) метода. Однако преобразование завершается успешно, если вы используете NumberStyles.AllowThousands флаг, как показано в следующем примере.

using System;
using System.Globalization;

public class Example
{
   public static void Main()
   {
      string value = "1,304";
      int number;
      IFormatProvider provider = CultureInfo.CreateSpecificCulture("en-US");
      if (Int32.TryParse(value, out number))
         Console.WriteLine($"{value} --> {number}");
      else
         Console.WriteLine($"Unable to convert '{value}'");

      if (Int32.TryParse(value, NumberStyles.Integer | NumberStyles.AllowThousands,
                        provider, out number))
         Console.WriteLine($"{value} --> {number}");
      else
         Console.WriteLine($"Unable to convert '{value}'");
   }
}
// The example displays the following output:
//       Unable to convert '1,304'
//       1,304 --> 1304
Imports System.Globalization

Module Example
    Public Sub Main()
        Dim value As String = "1,304"
        Dim number As Integer
        Dim provider As IFormatProvider = CultureInfo.CreateSpecificCulture("en-US")
        If Int32.TryParse(value, number) Then
            Console.WriteLine("{0} --> {1}", value, number)
        Else
            Console.WriteLine("Unable to convert '{0}'", value)
        End If

        If Int32.TryParse(value, NumberStyles.Integer Or NumberStyles.AllowThousands,
                          provider, number) Then
            Console.WriteLine("{0} --> {1}", value, number)
        Else
            Console.WriteLine("Unable to convert '{0}'", value)
        End If
    End Sub
End Module
' The example displays the following output:
'       Unable to convert '1,304'
'       1,304 --> 1304

Предупреждение

Операция разбора данных всегда использует соглашения о форматировании определенной культуры. Если не указать культурную среду при передаче объекта CultureInfo или NumberFormatInfo, используется культура, которая связана с текущим потоком.

В следующей таблице перечислены элементы NumberStyles перечисления и описывается влияние, которое они имеют в операции синтаксического анализа.

Значение NumberStyles Влияние на синтаксический анализ строки
NumberStyles.None Разрешены только числовые цифры.
NumberStyles.AllowDecimalPoint Разрешены десятичные разделители и дробные цифры. Для целочисленных значений допускается только ноль в виде дробной цифры. Допустимые десятичные разделители определяются свойством NumberFormatInfo.NumberDecimalSeparator или NumberFormatInfo.CurrencyDecimalSeparator.
NumberStyles.AllowExponent Символ e или E можно использовать для указания экспоненциальной нотации. Дополнительные сведения см. в разделе NumberStyles.
NumberStyles.AllowLeadingWhite Допускается наличие начальных пробелов.
NumberStyles.AllowTrailingWhite Допускаются конечные пробелы.
NumberStyles.AllowLeadingSign Положительный или отрицательный знак может предшествовать числовым цифрам.
NumberStyles.AllowTrailingSign Положительный или отрицательный знак может следовать числовым цифрам.
NumberStyles.AllowParentheses Скобки можно использовать для указания отрицательных значений.
NumberStyles.AllowThousands Разрешен разделитель групп. Символ разделителя группы определяется свойством NumberFormatInfo.NumberGroupSeparator или NumberFormatInfo.CurrencyGroupSeparator.
NumberStyles.AllowCurrencySymbol Разрешен символ валюты. Символ валюты определяется свойством NumberFormatInfo.CurrencySymbol .
NumberStyles.AllowHexSpecifier Строка для синтаксического анализа интерпретируется как шестнадцатеричное число. Он может включать шестнадцатеричные цифры 0-9, A-F и a-f. Этот флаг можно использовать только для синтаксического анализа целых значений.
NumberStyles.AllowBinarySpecifier Строка для разбора интерпретируется как двоичное число. Он может включать двоичные цифры 0 и 1. Этот флаг можно использовать только для синтаксического анализа целых значений.

Кроме того, перечисление NumberStyles предоставляет следующие составные стили, которые включают несколько NumberStyles флагов.

Составное значение NumberStyles Включает участников
NumberStyles.Integer Включает NumberStyles.AllowLeadingWhite, NumberStyles.AllowTrailingWhite и NumberStyles.AllowLeadingSign стили. Это стиль по умолчанию, используемый для синтаксического анализа целочисленных значений.
NumberStyles.Number Включает стили NumberStyles.AllowLeadingWhite, NumberStyles.AllowTrailingWhite, NumberStyles.AllowLeadingSign, NumberStyles.AllowTrailingSign, NumberStyles.AllowDecimalPoint и NumberStyles.AllowThousands.
NumberStyles.Float Включает в себя стили NumberStyles.AllowLeadingWhite, NumberStyles.AllowTrailingWhite, NumberStyles.AllowLeadingSign, NumberStyles.AllowDecimalPoint и NumberStyles.AllowExponent.
NumberStyles.Currency Включает все стили, кроме NumberStyles.AllowExponent и NumberStyles.AllowHexSpecifier.
NumberStyles.Any Включает все стили, кроме NumberStyles.AllowHexSpecifier.
NumberStyles.HexNumber Включает NumberStyles.AllowLeadingWhite, NumberStyles.AllowTrailingWhite и NumberStyles.AllowHexSpecifier стили.
NumberStyles.BinaryNumber Включает NumberStyles.AllowLeadingWhite, NumberStyles.AllowTrailingWhite и NumberStyles.AllowBinarySpecifier стили.

Разбор двоичных и шестнадцатеричных BigInteger объектов

При синтаксическом анализе BigInteger с флагами AllowHexSpecifier или AllowBinarySpecifier входная строка интерпретируется как шестнадцатеричное или двоичное число, совпадающее по длине со строкой. Например, синтаксический анализ "11" в виде двоичного числа BigInteger дает -1, так как это интерпретация 11 в качестве значения дополнения со знаком с ровно 2 цифрами. Если вы хотите положительный результат, добавьте ведущий 0, например "011" , который анализируется как 3.

Синтаксический анализ и цифры Юникода

Стандарт Юникода определяет кодовые точки для цифр в различных системах записи. Например, кодовые точки от U+0030 до U+0039 представляют основные латинские цифры от 0 до 9, кодовые точки от U+09E6 до U+09EF представляют цифры Bangla от 0 до 9, а кодовые точки от U+FF10 до U+FF19 представляют цифры Fullwidth от 0 до 9. Однако единственными числовыми цифрами, распознаваемыми методами синтаксического анализа, являются базовые латинские цифры 0-9 с точками кода от U+0030 до U+0039. Если методу синтаксического анализа передана строка, содержащая любые нецифровые символы, метод вызывает FormatExceptionисключение.

В следующем примере метод используется Int32.Parse для анализа строк, состоящих из цифр в разных системах записи. Как показано в выходных данных примера, попытка синтаксического анализа основных латинских цифр завершается успешно, но попытка проанализировать цифры двойной ширины, арабско-индийские и бенгальские завершается ошибкой.

using System;

public class Example
{
   public static void Main()
   {
      string value;
      // Define a string of basic Latin digits 1-5.
      value = "\u0031\u0032\u0033\u0034\u0035";
      ParseDigits(value);

      // Define a string of Fullwidth digits 1-5.
      value = "\uFF11\uFF12\uFF13\uFF14\uFF15";
      ParseDigits(value);

      // Define a string of Arabic-Indic digits 1-5.
      value = "\u0661\u0662\u0663\u0664\u0665";
      ParseDigits(value);

      // Define a string of Bangla digits 1-5.
      value = "\u09e7\u09e8\u09e9\u09ea\u09eb";
      ParseDigits(value);
   }

   static void ParseDigits(string value)
   {
      try {
         int number = Int32.Parse(value);
         Console.WriteLine($"'{value}' --> {number}");
      }
      catch (FormatException) {
         Console.WriteLine($"Unable to parse '{value}'.");
      }
   }
}
// The example displays the following output:
//       '12345' --> 12345
//       Unable to parse '12345'.
//       Unable to parse '١٢٣٤٥'.
//       Unable to parse '১২৩৪৫'.
Module Example
    Public Sub Main()
        Dim value As String
        ' Define a string of basic Latin digits 1-5.
        value = ChrW(&h31) + ChrW(&h32) + ChrW(&h33) + ChrW(&h34) + ChrW(&h35)
        ParseDigits(value)

        ' Define a string of Fullwidth digits 1-5.
        value = ChrW(&hff11) + ChrW(&hff12) + ChrW(&hff13) + ChrW(&hff14) + ChrW(&hff15)
        ParseDigits(value)

        ' Define a string of Arabic-Indic digits 1-5.
        value = ChrW(&h661) + ChrW(&h662) + ChrW(&h663) + ChrW(&h664) + ChrW(&h665)
        ParseDigits(value)

        ' Define a string of Bangla digits 1-5.
        value = ChrW(&h09e7) + ChrW(&h09e8) + ChrW(&h09e9) + ChrW(&h09ea) + ChrW(&h09eb)
        ParseDigits(value)
    End Sub

    Sub ParseDigits(value As String)
        Try
            Dim number As Integer = Int32.Parse(value)
            Console.WriteLine("'{0}' --> {1}", value, number)
        Catch e As FormatException
            Console.WriteLine("Unable to parse '{0}'.", value)
        End Try
    End Sub
End Module
' The example displays the following output:
'       '12345' --> 12345
'       Unable to parse '12345'.
'       Unable to parse '١٢٣٤٥'.
'       Unable to parse '১২৩৪৫'.

См. также