Analizar cadenas numéricas en .NET

Todos los tipos numéricos tienen dos métodos de análisis estáticos, Parse y TryParse, que puede usar para convertir la representación de cadena de un número en un tipo numérico. Estos métodos permiten analizar cadenas generadas mediante el uso de las cadenas de formato que se documentan en Cadenas con formato numérico estándar y Cadenas con formato numérico personalizado. De forma predeterminada, los métodos Parse y TryParse pueden convertir correctamente las cadenas que contienen dígitos decimales enteros solo en valores enteros. Pueden convertir correctamente las cadenas que contienen dígitos decimales enteros y fraccionarios, separadores de grupos y un separador decimal en valores de punto flotante. El método Parse produce una excepción si se produce un error en la operación, mientras que el método TryParse devuelve false.

Nota

A partir de .NET 7, los tipos numéricos de .NET también implementan la interfaz System.IParsable<TSelf> , que define los métodos IParsable<TSelf>.Parse y IParsable<TSelf>.TryParse .

Análisis y proveedores de formato

Normalmente, las representaciones de cadena de valores numéricos se diferencian en la referencia cultural. Los elementos de las cadenas numéricas, como los símbolos de moneda, los separadores de grupo (o millares) y los separadores decimales, varían según la referencia cultural. Los métodos de análisis usan implícita o explícitamente un proveedor de formato que reconoce estas variaciones específicas de la referencia cultural. Si no se especifica ningún proveedor de formato en una llamada al método Parse o TryParse, se usa el proveedor de formato asociado a la referencia cultural actual (el objeto NumberFormatInfo devuelto por la propiedad NumberFormatInfo.CurrentInfo).

Un proveedor de formato se representa mediante una implementación IFormatProvider. Esta interfaz tiene un solo miembro, el método GetFormat, cuyo único parámetro es un objeto Type que representa el tipo al que se va a dar formato. Este método devuelve el objeto que proporciona información de formato. .NET es compatible con las dos implementaciones IFormatProvider siguientes para analizar cadenas numéricas:

En el ejemplo siguiente se intenta convertir cada cadena de una matriz en un valor Double. Primero se intenta analizar la cadena mediante un proveedor de formato que refleja las convenciones de la referencia cultural Inglés (Estados Unidos). Si esta operación produce una excepción FormatException, se intenta analizar la cadena mediante un proveedor de formato que refleja las convenciones de la referencia cultural Francés (Francia).

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'.

Análisis y valores NumberStyles

Los elementos de estilo (como espacio en blanco, separadores de grupos y separador decimal) que la operación de análisis puede controlar se definen mediante un valor de enumeración NumberStyles. De forma predeterminada, las cadenas que representan valores enteros se analizan mediante el valor NumberStyles.Integer, que solo permite dígitos numéricos, espacio en blanco inicial y final, y un signo inicial. Las cadenas que representan valores de punto flotante se analizan mediante una combinación de valores NumberStyles.Float y NumberStyles.AllowThousands. Este estilo compuesto permite dígitos decimales junto con un espacio en blanco inicial y final, un signo inicial, un separador decimal, separador de grupos y un exponente. Al llamar a una sobrecarga del método Parse o TryParse que incluya un parámetro de tipo NumberStyles y configurar una o más marcas NumberStyles, puede controlar los elementos de estilo que pueden estar presentes en la cadena para que la operación de análisis se realice correctamente.

Por ejemplo, una cadena que contiene un separador de grupo no se puede convertir en un valor de Int32 mediante el método Int32.Parse(String) . Pero la conversión se realiza correctamente si usa la marca NumberStyles.AllowThousands, como se muestra en el ejemplo siguiente.

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

Advertencia

La operación de análisis siempre usa las convenciones de formato de una referencia cultural determinada. Si no pasa un objeto CultureInfo o NumberFormatInfo para especificar una referencia cultural, se usa la referencia cultural asociada al subproceso actual.

En la tabla siguiente se enumeran los miembros de la enumeración NumberStyles y se describe el efecto que tienen en la operación de análisis.

Valor NumberStyles Efecto en la cadena que se va a analizar
NumberStyles.None Solo se permiten los dígitos numéricos.
NumberStyles.AllowDecimalPoint Se permiten el separador decimal y los dígitos fraccionarios. En el caso de los valores enteros, solo se permite cero como dígito fraccionario. La propiedad NumberFormatInfo.NumberDecimalSeparator o NumberFormatInfo.CurrencyDecimalSeparator determina los separadores decimales válidos.
NumberStyles.AllowExponent Se puede usar el carácter "e" o "E" para indicar la notación exponencial. Para obtener más información, vea NumberStyles.
NumberStyles.AllowLeadingWhite Se permite el espacio en blanco inicial.
NumberStyles.AllowTrailingWhite Se permite el espacio en blanco final.
NumberStyles.AllowLeadingSign Un signo positivo o negativo puede preceder a los dígitos numéricos.
NumberStyles.AllowTrailingSign Un signo positivo o negativo puede seguir a los dígitos numéricos.
NumberStyles.AllowParentheses Se pueden usar paréntesis para indicar valores negativos.
NumberStyles.AllowThousands Se permite el separador de grupos. El carácter de separador de grupo viene determinado por la propiedad NumberFormatInfo.NumberGroupSeparator o NumberFormatInfo.CurrencyGroupSeparator.
NumberStyles.AllowCurrencySymbol Se permite el símbolo de moneda. El símbolo de moneda se define mediante la propiedad NumberFormatInfo.CurrencySymbol.
NumberStyles.AllowHexSpecifier La cadena que se va a analizar se interpreta como un número hexadecimal. Puede incluir los dígitos hexadecimales 0-9, A-F y a-f. Esta marca solo se puede usar para analizar valores enteros.

Además, la enumeración NumberStyles proporciona los siguientes estilos compuestos, que incluyen varias marcas NumberStyles.

Valor NumberStyles compuestos Miembros que incluye
NumberStyles.Integer Incluye los estilos NumberStyles.AllowLeadingWhite, NumberStyles.AllowTrailingWhite y NumberStyles.AllowLeadingSign. Este es el estilo predeterminado que se usa para analizar valores enteros.
NumberStyles.Number Incluye los estilos NumberStyles.AllowLeadingWhite, NumberStyles.AllowTrailingWhite, NumberStyles.AllowLeadingSign, NumberStyles.AllowTrailingSign, NumberStyles.AllowDecimalPoint y NumberStyles.AllowThousands.
NumberStyles.Float Incluye los estilos NumberStyles.AllowLeadingWhite, NumberStyles.AllowTrailingWhite, NumberStyles.AllowLeadingSign, NumberStyles.AllowDecimalPoint y NumberStyles.AllowExponent.
NumberStyles.Currency Incluye todos los estilos excepto NumberStyles.AllowExponent y NumberStyles.AllowHexSpecifier.
NumberStyles.Any Incluye todos los estilos excepto NumberStyles.AllowHexSpecifier.
NumberStyles.HexNumber Incluye los estilos NumberStyles.AllowLeadingWhite, NumberStyles.AllowTrailingWhite y NumberStyles.AllowHexSpecifier.

Análisis y dígitos Unicode

El estándar Unicode define puntos de código para dígitos de diferentes sistemas de escritura. Por ejemplo, los puntos de código de U+0030 a U+0039 representan los dígitos latinos básicos del 0 al 9, los puntos de código de U+09E6 a U+09EF representan los dígitos de bengalí del 0 al 9, y los puntos de código de U+FF10 a U+FF19 representan los dígitos de ancho completo del 0 al 9. Pero los únicos dígitos numéricos que reconocen los métodos de análisis son los dígitos latinos básicos del 0 al 9 con puntos de código de U+0030 a U+0039. Si se pasa a un método de análisis numérico una cadena que contenga cualquier otro dígito, el método producirá una excepción FormatException.

En el ejemplo siguiente se usa el método Int32.Parse para analizar las cadenas que se componen de dígitos en sistemas de escritura diferentes. Como muestra la salida del ejemplo, el intento de analizar los dígitos latinos básicos se realiza correctamente, pero se produce un error en el intento de analizar los dígitos de ancho completo, árabe-hindús y de bengalí.

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 '১২৩৪৫'.

Vea también