.NET での数値文字列の解析

すべての数値型には、2 つの静的解析メソッド (ParseTryParse) があり、数字の文字列形式を数値型に変換するために使用できます。 これらのメソッドでは、標準の数値書式指定文字列カスタム数値書式指定文字列で記述されている書式指定文字列を使用して、生成された文字列を解析できます。 既定では、ParseTryParse メソッドは、10 進数の整数を含む文字列を整数値のみに正常に変換することができます。 これらのメソッドは、整数部と小数部、グループ区切り、および小数点記号を含む文字列を浮動小数点値に正常に変換できます。 TryParse メソッドが false を返すのに対して、Parse メソッドは操作が失敗した場合に例外をスローします。

注意

.NET 7 以降では、.NET の数値型でも System.IParsable<TSelf> インターフェイスが実装されます。このインターフェイスによって、IParsable<TSelf>.Parse メソッドと IParsable<TSelf>.TryParse メソッドが定義されます。

解析と書式プロバイダー

通常、数値の文字列形式はカルチャによって異なります。 通貨記号、グループ (または千単位) 区切り、および小数点記号などの数値文字列の要素は、すべてカルチャによって異なります。 暗黙的または明示的のいずれかの解析メソッドでは、これらのカルチャ固有のバリエーションを認識する書式プロバイダーを使用します。 書式プロバイダーが Parse または TryParse メソッドの呼び出しで指定されない場合、現在のカルチャ (NumberFormatInfo.CurrentInfo プロパティで返される NumberFormatInfo オブジェクト) と関連付けられた書式プロバイダーが使用されます。

書式プロバイダーは、IFormatProvider 実装によって示されます。 このインターフェイスには、1 つのメンバー (GetFormat メソッド) があり、その 1 つのパラメーターは、書式設定される型を示す Type オブジェクトです。 このメソッドは、書式情報を示すオブジェクトを返します。 .NET では、数値文字列を解析するために、次の 2 つの 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 値の組み合わせを使用して解析されます。この複合スタイルは、先頭と末尾の空白、先頭の符号、小数点記号、グループ区切り、および指数と共に 10 進数を許可します。 NumberStyles 型のパラメーターを含む、Parse または TryParse メソッドのオーバーロードを呼び出し、1 つ以上の NumberStyles フラグを設定すると、解析操作が成功するように、文字列で示すことができるスタイル要素を制御することができます。

たとえば、グループ区切りを含む文字列は、Int32.Parse(String) メソッドを使用して、Int32 値に変換することはできません。 ただし、次の例に示すように、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 小数点の記号と桁数が許可されます。 整数値の場合、0 のみが小数点の桁数として許可されます。 有効な小数点は 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 解析する文字列は、16 進数として解釈されます。 これには、16 進数の値の 0 ~ 9、A ~ F、a ~ f を含めることができます。 このフラグは、整数値を解析するためにだけに使用できます。

さらに、NumberStyles 列挙体は、複数の NumberStyles フラグを含む、次の複合スタイルを指定します。

複合 NumberStyles 値 数値を含む
NumberStyles.Integer NumberStyles.AllowLeadingWhiteNumberStyles.AllowTrailingWhiteNumberStyles.AllowLeadingSign スタイルが含まれます。 これは、整数値を解析するために使用される既定のスタイルです。
NumberStyles.Number NumberStyles.AllowLeadingWhiteNumberStyles.AllowTrailingWhiteNumberStyles.AllowLeadingSignNumberStyles.AllowTrailingSignNumberStyles.AllowDecimalPointNumberStyles.AllowThousands スタイルが含まれます。
NumberStyles.Float NumberStyles.AllowLeadingWhiteNumberStyles.AllowTrailingWhiteNumberStyles.AllowLeadingSignNumberStyles.AllowDecimalPointNumberStyles.AllowExponent スタイルが含まれます。
NumberStyles.Currency NumberStyles.AllowExponentNumberStyles.AllowHexSpecifier を除くすべてのスタイルが含まれます。
NumberStyles.Any NumberStyles.AllowHexSpecifier を除くすべてのスタイルが含まれます。
NumberStyles.HexNumber NumberStyles.AllowLeadingWhiteNumberStyles.AllowTrailingWhiteNumberStyles.AllowHexSpecifier スタイルが含まれます。

解析と Unicode 数字

Unicode 標準では、さまざまな書記体系で数字のコード ポイントを定義します。 たとえば、U+0030 ~ U+0039 のコード ポイントは、0 ~ 9 の基本ラテンの数字を示し、U+09E6 ~ U+09EF のコード ポイントは、0 ~ 9 の数字のベンガル語の数字を示し、U+FF10 ~ U+FF19 のコード ポイントは、0 ~ 9 の全角の数字を示します。 ただし、解析メソッドで認識される数字は、U+0030 ~ U+0039 のコード ポイントの基本ラテンの数字 0 ~ 9 のみです。 数値解析メソッドがその他の数字を含む文字列を渡す場合、メソッドは 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 '১২৩৪৫'.

関連項目