Анализ числовых строк в .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("{0}: {1} --> {2}", culture.Name, value, number);
         }
         catch (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 (FormatException) {
               Console.WriteLine("{0}: Unable to parse '{1}'.",
                                 culture.Name, 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("{0} --> {1}", value, number);
      else
         Console.WriteLine("Unable to convert '{0}'", value);

      if (Int32.TryParse(value, NumberStyles.Integer | NumberStyles.AllowThousands,
                        provider, out number))
         Console.WriteLine("{0} --> {1}", value, number);
      else
         Console.WriteLine("Unable to convert '{0}'", 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 предоставляет следующие смешанные стили, включающие несколько флагов 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.

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

Стандарт Юникод определяет кодовые точки для цифр в различных системах письма. Например, кодовые точки в диапазоне от U+0030 до U+0039 представляют основные цифры от 0 до 9, кодовые точки в диапазоне от U+09E6 до U+09EF представляют бенгальские цифры от 0 до 9, а кодовые точки в диапазоне от U+FF10 до U+FF19 представляют полноширинные цифры от 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("'{0}' --> {1}", value, number);
      }
      catch (FormatException) {
         Console.WriteLine("Unable to parse '{0}'.", 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 '১২৩৪৫'.

См. также