Share via


Classe NumberFormatInfo

Este artigo fornece observações complementares à documentação de referência para essa API.

A classe NumberFormatInfo contém informações específicas da cultura que são usadas quando você formata e analisa valores numéricos. Essas informações incluem o símbolo da moeda, o símbolo decimal, o símbolo de separação de grupos e os símbolos para sinais positivos e negativos.

Criar uma instância de um objeto NumberFormatInfo

Você pode criar uma instância de um objeto NumberFormatInfo que representa as convenções de formatação da cultura atual, da cultura invariável, de uma cultura específica ou de uma cultura neutra.

Criar uma instância de um objeto NumberFormatInfo para a cultura atual

Você pode criar uma instância de um objeto NumberFormatInfo para a cultura atual de uma das seguintes maneiras. Em cada caso, o objeto NumberFormatInfo retornado é somente leitura.

O exemplo a seguir usa essas três maneiras para criar objetos NumberFormatInfo que representam as convenções de formatação da cultura atual. Ele também recupera o valor da propriedade IsReadOnly para ilustrar que cada objeto é somente leitura.

using System;
using System.Globalization;

public class InstantiateEx1
{
    public static void Main()
    {
        NumberFormatInfo current1 = CultureInfo.CurrentCulture.NumberFormat;
        Console.WriteLine(current1.IsReadOnly);

        NumberFormatInfo current2 = NumberFormatInfo.CurrentInfo;
        Console.WriteLine(current2.IsReadOnly);

        NumberFormatInfo current3 = NumberFormatInfo.GetInstance(CultureInfo.CurrentCulture);
        Console.WriteLine(current3.IsReadOnly);
    }
}
// The example displays the following output:
//       True
//       True
//       True

Você pode criar um objeto NumberFormatInfo gravável que represente as convenções da cultura atual de uma das seguintes maneiras:

O exemplo a seguir ilustra essas duas formas de instanciar um objeto NumberFormatInfo e exibe o valor da sua propriedade IsReadOnly para mostrar que o objeto não é somente leitura.

using System;
using System.Globalization;

public class InstantiateEx2
{
    public static void Main()
    {
        NumberFormatInfo current1 = NumberFormatInfo.CurrentInfo;
        current1 = (NumberFormatInfo)current1.Clone();
        Console.WriteLine(current1.IsReadOnly);

        CultureInfo culture2 = CultureInfo.CreateSpecificCulture(CultureInfo.CurrentCulture.Name);
        NumberFormatInfo current2 = culture2.NumberFormat;
        Console.WriteLine(current2.IsReadOnly);
    }
}
// The example displays the following output:
//       False
//       False

Observe que o sistema operacional Windows permite que o usuário substitua alguns dos valores de propriedade NumberFormatInfo usados em operações de formatação e análise numérica por meio do item Região e Idioma no Painel de Controle. Por exemplo, um usuário cuja cultura é o inglês (Estados Unidos) pode optar por exibir valores monetários como 1.1 USD em vez do padrão de $1.1. Todos os objetos NumberFormatInfo recuperados das formas discutidas anteriormente refletem essas substituições do usuário. Se isso não for desejável, é possível criar um objeto NumberFormatInfo que não reflita as substituições do usuário (e que também seja de leitura/gravação em vez de somente leitura) chamando o construtor CultureInfo.CultureInfo(String, Boolean) e fornecendo um valor false para o argumento useUserOverride. O exemplo a seguir fornece uma ilustração de um sistema cuja cultura atual é o inglês (Estados Unidos) e cujo símbolo de moeda foi alterado do padrão $ para USD.

using System;
using System.Globalization;

public class InstantiateEx3
{
    public static void Main()
    {
        CultureInfo culture;
        NumberFormatInfo nfi;

        culture = CultureInfo.CurrentCulture;
        nfi = culture.NumberFormat;
        Console.WriteLine("Culture Name:    {0}", culture.Name);
        Console.WriteLine("User Overrides:  {0}", culture.UseUserOverride);
        Console.WriteLine("Currency Symbol: {0}\n", culture.NumberFormat.CurrencySymbol);

        culture = new CultureInfo(CultureInfo.CurrentCulture.Name, false);
        Console.WriteLine("Culture Name:    {0}", culture.Name);
        Console.WriteLine("User Overrides:  {0}", culture.UseUserOverride);
        Console.WriteLine("Currency Symbol: {0}", culture.NumberFormat.CurrencySymbol);
    }
}
// The example displays the following output:
//       Culture Name:    en-US
//       User Overrides:  True
//       Currency Symbol: USD
//
//       Culture Name:    en-US
//       User Overrides:  False
//       Currency Symbol: $

Se a propriedade CultureInfo.UseUserOverride for definida como true, as propriedades CultureInfo.DateTimeFormat, CultureInfo.NumberFormat e CultureInfo.TextInfo também serão recuperadas das configurações do usuário. Se as configurações do usuário forem incompatíveis com a cultura associada ao objeto CultureInfo (por exemplo, se o calendário selecionado não for um dos calendários listados pela propriedade OptionalCalendars), os resultados dos métodos e os valores das propriedades serão indefinidos.

Criar uma instância de um objeto NumberFormatInfo para a cultura invariável

A cultura invariável representa uma cultura insensível. Ele se baseia no idioma inglês, mas não em nenhum país/região de língua inglesa específico. Embora os dados de culturas específicas possam ser dinâmicos e alterados para refletir novas convenções culturais ou preferências do usuário, os dados da cultura invariável não são alterados. Um objeto NumberFormatInfo que representa as convenções de formatação da cultura invariável pode ser usado para operações de formatação nas quais as cadeias de resultados não devem variar de acordo com a cultura.

Você pode criar uma instância de um objeto NumberFormatInfo que representa as convenções de formatação da cultura invariável das seguintes maneiras:

O exemplo a seguir usa cada um desses métodos para criar uma instância de um objeto NumberFormatInfo que representa a cultura invariável. Em seguida, ele indica se o objeto é somente leitura,

using System;
using System.Globalization;

public class InstantiateEx4
{
    public static void Main()
    {
        NumberFormatInfo nfi;

        nfi = System.Globalization.NumberFormatInfo.InvariantInfo;
        Console.WriteLine(nfi.IsReadOnly);

        nfi = CultureInfo.InvariantCulture.NumberFormat;
        Console.WriteLine(nfi.IsReadOnly);

        nfi = new NumberFormatInfo();
        Console.WriteLine(nfi.IsReadOnly);
    }
}
// The example displays the following output:
//       True
//       True
//       False

Criar uma instância de um objeto NumberFormatInfo para uma cultura específica

Uma cultura específica representa um idioma que é falado em um determinado país/região. Por exemplo, en-US é uma cultura específica que representa o idioma inglês falado nos Estados Unidos, e en-CA é uma cultura específica que representa o idioma inglês falado no Canadá. Você pode criar uma instância de um objeto NumberFormatInfo que represente as convenções de formatação de uma cultura específica das seguintes maneiras:

O exemplo a seguir usa essas quatro maneiras para criar um objeto NumberFormatInfo que reflete as convenções de formatação da cultura indonésia (Indonesia). Ele também indica se cada objeto é somente leitura.

using System;
using System.Globalization;

public class InstantiateEx5
{
    public static void Main()
    {
        CultureInfo culture;
        NumberFormatInfo nfi;

        nfi = CultureInfo.GetCultureInfo("id-ID").NumberFormat;
        Console.WriteLine("Read-only: {0}", nfi.IsReadOnly);

        culture = new CultureInfo("id-ID");
        nfi = NumberFormatInfo.GetInstance(culture);
        Console.WriteLine("Read-only: {0}", nfi.IsReadOnly);

        culture = CultureInfo.CreateSpecificCulture("id-ID");
        nfi = culture.NumberFormat;
        Console.WriteLine("Read-only: {0}", nfi.IsReadOnly);

        culture = new CultureInfo("id-ID");
        nfi = culture.NumberFormat;
        Console.WriteLine("Read-only: {0}", nfi.IsReadOnly);
    }
}
// The example displays the following output:
//       Read-only: True
//       Read-only: False
//       Read-only: False
//       Read-only: False

Criar uma instância de um objeto NumberFormatInfo para uma cultura neutra

Uma cultura neutra representa uma cultura ou um idioma que é independente de um país/região. Normalmente, é o pai de uma ou mais culturas específicas. Por exemplo, fr é uma cultura neutra para o idioma francês e o pai da cultura fr-FR. Você cria um objeto NumberFormatInfo que representa as convenções de formatação de uma cultura neutra da mesma forma que cria um objeto NumberFormatInfo que representa as convenções de formatação de uma cultura específica.

No entanto, por ser independente de um país/região específico, uma cultura neutra carece de informações de formatação específicas da cultura. Em vez de preencher o objeto NumberFormatInfo com valores genéricos, o .NET retorna um objeto NumberFormatInfo que reflete as convenções de formatação de uma cultura específica que é filha da cultura neutra. Por exemplo, o objeto NumberFormatInfo para a cultura neutra en reflete as convenções de formatação da cultura en-US e o objeto NumberFormatInfo para a cultura fr reflete as convenções de formatação da cultura fr-FR.

Você pode usar um código como o seguinte para determinar quais convenções de formatação de cultura específica cada cultura neutra representa.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;

public class InstantiateEx6
{
    public static void Main()
    {
        // Get all the neutral cultures
        List<String> names = new List<String>();
        Array.ForEach(CultureInfo.GetCultures(CultureTypes.NeutralCultures),
                      culture => names.Add(culture.Name));
        names.Sort();
        foreach (var name in names)
        {
            // Ignore the invariant culture.
            if (name == "") continue;

            ListSimilarChildCultures(name);
        }
    }

    private static void ListSimilarChildCultures(string name)
    {
        // Create the neutral NumberFormatInfo object.
        NumberFormatInfo nfi = CultureInfo.GetCultureInfo(name).NumberFormat;
        // Retrieve all specific cultures of the neutral culture.
        CultureInfo[] cultures = Array.FindAll(CultureInfo.GetCultures(CultureTypes.SpecificCultures),
                                 culture => culture.Name.StartsWith(name + "-", StringComparison.OrdinalIgnoreCase));
        // Create an array of NumberFormatInfo properties
        PropertyInfo[] properties = typeof(NumberFormatInfo).GetProperties(BindingFlags.Instance | BindingFlags.Public);
        bool hasOneMatch = false;

        foreach (var ci in cultures)
        {
            bool match = true;
            // Get the NumberFormatInfo for a specific culture.
            NumberFormatInfo specificNfi = ci.NumberFormat;
            // Compare the property values of the two.
            foreach (var prop in properties)
            {
                // We're not interested in the value of IsReadOnly.
                if (prop.Name == "IsReadOnly") continue;

                // For arrays, iterate the individual elements to see if they are the same.
                if (prop.PropertyType.IsArray)
                {
                    IList nList = (IList)prop.GetValue(nfi, null);
                    IList sList = (IList)prop.GetValue(specificNfi, null);
                    if (nList.Count != sList.Count)
                    {
                        match = false;
                        break;
                    }

                    for (int ctr = 0; ctr < nList.Count; ctr++)
                    {
                        if (!nList[ctr].Equals(sList[ctr]))
                        {
                            match = false;
                            break;
                        }
                    }
                }
                else if (!prop.GetValue(specificNfi).Equals(prop.GetValue(nfi)))
                {
                    match = false;
                    break;
                }
            }
            if (match)
            {
                Console.WriteLine("NumberFormatInfo object for '{0}' matches '{1}'",
                                          name, ci.Name);
                hasOneMatch = true;
            }
        }
        if (!hasOneMatch)
            Console.WriteLine("NumberFormatInfo object for '{0}' --> No Match", name);

        Console.WriteLine();
    }
}

Dados dinâmicos

Os dados específicos da cultura para a formatação de valores numéricos fornecidos pela classe NumberFormatInfo são dinâmicos, assim como os dados culturais fornecidos pela classe CultureInfo. Você não deve fazer nenhuma suposição sobre a estabilidade dos valores dos objetos NumberFormatInfo que estão associados a objetos CultureInfo específicos. Somente os dados fornecidos pela cultura invariável e seu objeto NumberFormatInfo associado são estáveis. Outros dados podem ser alterados entre as sessões do aplicativo, ou mesmo em uma única sessão, pelos seguintes motivos:

  • Atualizações do sistema. As preferências culturais, como o símbolo da moeda ou os formatos da moeda, sofrem alterações ao longo do tempo. Quando isso acontece, o Windows Update inclui alterações no valor da propriedade NumberFormatInfo para uma cultura específica.

  • Culturas de substituição. A classe CultureAndRegionInfoBuilder pode ser usada para substituir os dados de uma cultura existente.

  • Alterações em cascata nos valores das propriedades. Várias propriedades relacionadas à cultura podem ser alteradas em tempo de execução, o que, por sua vez, faz com que os dados NumberFormatInfo sejam alterados. Por exemplo, a cultura atual pode ser alterada de maneira programática ou por meio da ação do usuário. Quando isso acontece, o objeto NumberFormatInfo retornado pela propriedade CurrentInfo é alterado para um objeto associado à cultura atual.

  • Preferências do usuário. Os usuários do seu aplicativo podem substituir alguns dos valores associados à cultura atual do sistema por meio das opções de região e idioma no Painel de Controle. Por exemplo, os usuários podem escolher um símbolo de moeda diferente ou um símbolo de separador decimal diferente. Se a propriedade CultureInfo.UseUserOverride for definida como true (seu valor padrão), as propriedades do objeto NumberFormatInfo também serão recuperadas das configurações do usuário.

Todas as propriedades substituíveis pelo usuário de um objeto NumberFormatInfo são inicializadas quando o objeto é criado. Ainda existe a possibilidade de inconsistência, pois nem a criação do objeto nem o processo de substituição do usuário são atômicos, e os valores relevantes podem ser alterados durante a criação do objeto. Entretanto, essas inconsistências devem ser extremamente raras.

É possível controlar se as substituições do usuário são refletidas em objetos NumberFormatInfo que representam a mesma cultura que a cultura atual. A tabela a seguir lista as maneiras pelas quais um objeto NumberFormatInfo pode ser recuperado e indica se o objeto resultante reflete as substituições do usuário.

Origem do objeto CultureInfo e NumberFormatInfo Reflete as substituições do usuário
Propriedade CultureInfo.CurrentCulture.NumberFormat Sim
Propriedade NumberFormatInfo.CurrentInfo Sim
Método CultureInfo.CreateSpecificCulture Sim
Método CultureInfo.GetCultureInfo Não
Construtor CultureInfo(String) Sim
Construtor CultureInfo.CultureInfo(String, Boolean) Depende do valor do parâmetro useUserOverride

A menos que haja um motivo convincente para fazer o contrário, você deve respeitar as substituições do usuário ao usar o objeto NumberFormatInfo em aplicativos clientes para formatar e analisar a entrada do usuário ou exibir dados numéricos. Para aplicativos de servidor ou aplicativos não supervisionados, você não deve respeitar as substituições do usuário. No entanto, se estiver usando o objeto NumberFormatInfo explícita ou implicitamente para manter dados numéricos em forma de cadeia de caracteres, você deverá usar um objeto NumberFormatInfo que reflita as convenções de formatação da cultura invariável ou especificar uma cadeia de formato numérico personalizada que será usada independentemente da cultura.

IFormatProvider, NumberFormatInfo e formatação numérica

Um objeto NumberFormatInfo é usado implícita ou explicitamente em todas as operações de formatação numérica. Isso inclui as chamadas para os seguintes métodos:

Todas as operações de formatação numérica fazem uso de uma implementação IFormatProvider. A interface IFormatProvider inclui um único método, GetFormat(Type). Esse é um método de retorno de chamada que recebe um objeto Type que representa o tipo necessário para fornecer informações de formatação. O método é responsável por retornar uma instância desse tipo ou null, se não puder fornecer uma instância do tipo. O .NET oferece duas implementações IFormatProvider para formatação de números:

Se uma implementação IFormatProvider não for fornecida explicitamente a um método de formatação, será usado um objeto CultureInfo retornado pela propriedade CultureInfo.CurrentCulture que representa a cultura atual.

O exemplo a seguir ilustra a relação entre a interface IFormatProvider e a classe NumberFormatInfo em operações de formatação, definindo uma implementação IFormatProvider personalizada. Seu método GetFormat exibe o nome do tipo do objeto solicitado pela operação de formatação. Se a interface estiver solicitando um objeto NumberFormatInfo, esse método fornecerá o objeto NumberFormatInfo para a cultura atual. Como mostra a saída do exemplo, o método Decimal.ToString(IFormatProvider) solicita um objeto NumberFormatInfo para fornecer informações de formatação, enquanto o método String.Format(IFormatProvider, String, Object[]) solicita objetos NumberFormatInfo e DateTimeFormatInfo, bem como uma implementação ICustomFormatter.

using System;
using System.Globalization;

public class CurrentCultureFormatProvider : IFormatProvider
{
    public Object GetFormat(Type formatType)
    {
        Console.WriteLine("Requesting an object of type {0}",
                          formatType.Name);
        if (formatType == typeof(NumberFormatInfo))
            return NumberFormatInfo.CurrentInfo;
        else if (formatType == typeof(DateTimeFormatInfo))
            return DateTimeFormatInfo.CurrentInfo;
        else
            return null;
    }
}

public class FormatProviderEx
{
    public static void Main()
    {
        Decimal amount = 1203.541m;
        string value = amount.ToString("C2", new CurrentCultureFormatProvider());
        Console.WriteLine(value);
        Console.WriteLine();
        string composite = String.Format(new CurrentCultureFormatProvider(),
                                         "Date: {0}   Amount: {1}   Description: {2}",
                                         DateTime.Now, 1264.03m, "Service Charge");
        Console.WriteLine(composite);
        Console.WriteLine();
    }
}
// The example displays output like the following:
//    Requesting an object of type NumberFormatInfo
//    $1,203.54
//
//    Requesting an object of type ICustomFormatter
//    Requesting an object of type DateTimeFormatInfo
//    Requesting an object of type NumberFormatInfo
//    Date: 11/15/2012 2:00:01 PM   Amount: 1264.03   Description: Service Charge

Se uma implementação IFormatProvider não for explicitamente fornecida em uma chamada de método de formatação numérica, o método chamará o método CultureInfo.CurrentCulture.GetFormat, que retorna o objeto NumberFormatInfo que corresponde à cultura atual.

Cadeias de caracteres de formato e propriedades de NumberFormatInfo

Toda operação de formatação usa uma cadeia de caracteres de formato numérico padrão ou personalizado para produzir uma cadeia de caracteres de resultado a partir de um número. Em alguns casos, o uso de uma cadeia de caracteres de formato para produzir uma cadeia de caracteres de resultado é explícito, como no exemplo a seguir. Esse código chama o método Decimal.ToString(IFormatProvider) para converter um valor Decimal em várias representações de cadeia de caracteres diferentes, usando as convenções de formatação da cultura en-US.

using System;
using System.Globalization;

public class PropertiesEx1
{
    public static void Main()
    {
        string[] formatStrings = { "C2", "E1", "F", "G3", "N",
                                 "#,##0.000", "0,000,000,000.0##" };
        CultureInfo culture = CultureInfo.CreateSpecificCulture("en-US");
        Decimal[] values = { 1345.6538m, 1921651.16m };

        foreach (var value in values)
        {
            foreach (var formatString in formatStrings)
            {
                string resultString = value.ToString(formatString, culture);
                Console.WriteLine("{0,-18} -->  {1}", formatString, resultString);
            }
            Console.WriteLine();
        }
    }
}
// The example displays the following output:
//       C2                 -->  $1,345.65
//       E1                 -->  1.3E+003
//       F                  -->  1345.65
//       G3                 -->  1.35E+03
//       N                  -->  1,345.65
//       #,##0.000          -->  1,345.654
//       0,000,000,000.0##  -->  0,000,001,345.654
//
//       C2                 -->  $1,921,651.16
//       E1                 -->  1.9E+006
//       F                  -->  1921651.16
//       G3                 -->  1.92E+06
//       N                  -->  1,921,651.16
//       #,##0.000          -->  1,921,651.160
//       0,000,000,000.0##  -->  0,001,921,651.16

Em outros casos, o uso de uma cadeia de caracteres de formato é implícito. Por exemplo, nas seguintes chamadas de método para o método Decimal.ToString() padrão ou sem parâmetros, o valor da instância Decimal é formatado usando o especificador de formato geral ("G") e as convenções da cultura atual, que, nesse caso, é a cultura en-US.

using System;

public class PropertiesEx2
{
    public static void Main()
    {
        Decimal[] values = { 1345.6538m, 1921651.16m };

        foreach (var value in values)
        {
            string resultString = value.ToString();
            Console.WriteLine(resultString);
            Console.WriteLine();
        }
    }
}
// The example displays the following output:
//       1345.6538
//
//       1921651.16

Cada cadeia de caracteres de formato numérico padrão usa uma ou mais propriedades NumberFormatInfo para determinar o padrão ou os símbolos usados na cadeia de caracteres de resultado. Da mesma forma, cada especificador de formato numérico personalizado, exceto "0" e "#", insere símbolos na cadeia de resultados que são definidos pelas propriedades NumberFormatInfo. A tabela a seguir lista os especificadores de formato numérico padrão e personalizado e suas propriedades NumberFormatInfo associadas. Para alterar a aparência da cadeia de resultados de uma cultura específica, confira a seção Modificar propriedades do NumberFormatInfo. Para obter detalhes sobre o uso desses especificadores de formato, confira Cadeias de Caracteres de Formato Numérico Padrão e Cadeias de Caracteres de Formato Numérico Personalizado.

Especificador de formato Propriedades associadas
"C" ou "c" (especificador de formato de moeda) CurrencyDecimalDigits, para definir o número padrão de dígitos fracionários.

CurrencyDecimalSeparator, para definir o símbolo de separação decimal.

CurrencyGroupSeparator, para definir o separador de grupos ou milhares.

CurrencyGroupSizes, para definir os tamanhos dos grupos integrais.

CurrencyNegativePattern, para definir o padrão de valores monetários negativos.

CurrencyPositivePattern, para definir o padrão de valores monetários positivos.

CurrencySymbol, para definir o símbolo da moeda.

NegativeSign, para definir o símbolo de sinal negativo.
"D" ou "d" (especificador de formato decimal) NegativeSign, para definir o símbolo de sinal negativo.
"E" ou "e" (especificador de formato exponencial ou científico) NegativeSign, para definir o símbolo de sinal negativo na mantissa e no expoente.

NumberDecimalSeparator, para definir o símbolo separador decimal.

PositiveSign, para definir o símbolo de sinal positivo no expoente.
"F" ou "f" (especificador de formato de ponto fixo) NegativeSign, para definir o símbolo de sinal negativo.

NumberDecimalDigits, para definir o número padrão de dígitos fracionários.

NumberDecimalSeparator, para definir o símbolo separador decimal.
"G" ou "g" (especificador de formato geral) NegativeSign, para definir o símbolo de sinal negativo.

NumberDecimalSeparator, para definir o símbolo separador decimal.

PositiveSign, para definir o símbolo de sinal positivo para cadeias de resultados em formato exponencial.
"N" ou "n" (especificador de formato de número) NegativeSign, para definir o símbolo de sinal negativo.

NumberDecimalDigits, para definir o número padrão de dígitos fracionários.

NumberDecimalSeparator, para definir o símbolo separador decimal.

NumberGroupSeparator, para definir o símbolo de separador de grupo (milhares).

NumberGroupSizes, para definir o número de dígitos integrais em um grupo.

NumberNegativePattern, para definir o formato de valores negativos.
"P" ou "p" (especificador de formato de porcentagem) NegativeSign, para definir o símbolo de sinal negativo.

PercentDecimalDigits, para definir o número padrão de dígitos fracionários.

PercentDecimalSeparator, para definir o símbolo de separação decimal.

PercentGroupSeparator, para definir o símbolo separador de grupo.

PercentGroupSizes, para definir o número de dígitos integrais em um grupo.

PercentNegativePattern, para definir o posicionamento do símbolo de porcentagem e do símbolo de negativo para valores negativos.

PercentPositivePattern, para definir o posicionamento do símbolo de porcentagem para valores positivos.

PercentSymbol, para definir o símbolo de porcentagem.
"R" ou "r" (especificador de formato de ida e volta) NegativeSign, para definir o símbolo de sinal negativo.

NumberDecimalSeparator, para definir o símbolo separador decimal.

PositiveSign, para definir o símbolo de sinal positivo em um expoente.
"X" ou "x" (especificador de formato hexadecimal) Nenhum.
"." (especificador de formato personalizado de ponto decimal) NumberDecimalSeparator, para definir o símbolo separador decimal.
"," (especificador de formato personalizado do separador de grupo) NumberGroupSeparator, para definir o símbolo separador de grupo (milhares).
"%" (especificador de formato personalizado do espaço reservado para porcentagem) PercentSymbol, para definir o símbolo de porcentagem.
"‰" (especificador de formato personalizado do espaço reservado por milha) PerMilleSymbol, para definir o símbolo por milha.
"E" (especificador de formato personalizado de notação exponencial) NegativeSign, para definir o símbolo de sinal negativo na mantissa e no expoente.

PositiveSign, para definir o símbolo de sinal positivo no expoente.

Observe que a classe NumberFormatInfo inclui uma propriedade NativeDigits que especifica os dígitos de base 10 usados por uma cultura específica. No entanto, a propriedade não é usada em operações de formatação; somente os dígitos Latinos Básicos de 0 (U+0030) a 9 (U+0039) são usados na cadeia de caracteres de resultados. Além disso, para os valores Single e Double de NaN, PositiveInfinity e NegativeInfinity, a cadeia de caracteres de resultado consiste exclusivamente nos símbolos definidos pelas propriedades NaNSymbol, PositiveInfinitySymbol e NegativeInfinitySymbol, respectivamente.

Modificar as propriedades do NumberFormatInfo

Você pode modificar as propriedades de um objeto NumberFormatInfo para personalizar a cadeia de resultados produzida em uma operação de formatação numérica. Para fazer isso:

  1. Crie uma cópia de leitura/gravação de um objeto NumberFormatInfo cujas convenções de formatação você deseja modificar. Para obter mais informações, consulte a seção Criar uma instância de um objeto NumberFormatInfo.

  2. Modificar a propriedade ou as propriedades usadas para produzir a cadeia de caracteres do resultado desejado. Para obter informações sobre como os métodos de formatação usam propriedades NumberFormatInfo para definir cadeias de resultados, confira a seção Formatar cadeias de caracteres e propriedades NumberFormatInfo.

  3. Use o objeto NumberFormatInfo personalizado como o argumento IFormatProvider nas chamadas para métodos de formatação.

Observação

Em vez de modificar dinamicamente os valores de propriedade de uma cultura cada vez que um aplicativo é iniciado, você pode usar a classe CultureAndRegionInfoBuilder para definir uma cultura personalizada (uma cultura que tem um nome exclusivo e que complementa as culturas existentes) ou uma cultura de substituição (uma que é usada em vez de uma cultura específica).

As seções a seguir fornecem alguns exemplos.

Modificar o símbolo e o padrão da moeda

O exemplo a seguir modifica um objeto NumberFormatInfo que representa as convenções de formatação da cultura en-US. Ele atribui o símbolo de moeda ISO-4217 à propriedade CurrencySymbol e define um padrão para valores de moeda que consiste no símbolo de moeda seguido por um espaço e um valor numérico.

using System;
using System.Globalization;

public class Example
{
    public static void Main()
    {
        // Retrieve a writable NumberFormatInfo object.
        CultureInfo enUS = CultureInfo.CreateSpecificCulture("en-US");
        NumberFormatInfo nfi = enUS.NumberFormat;

        // Use the ISO currency symbol instead of the native currency symbol.
        nfi.CurrencySymbol = (new RegionInfo(enUS.Name)).ISOCurrencySymbol;
        // Change the positive currency pattern to <code><space><value>.
        nfi.CurrencyPositivePattern = 2;
        // Change the negative currency pattern to <code><space><sign><value>.
        nfi.CurrencyNegativePattern = 12;

        // Produce the result strings by calling ToString.
        Decimal[] values = { 1065.23m, 19.89m, -.03m, -175902.32m };
        foreach (var value in values)
            Console.WriteLine(value.ToString("C", enUS));

        Console.WriteLine();

        // Produce the result strings by calling a composite formatting method.
        foreach (var value in values)
            Console.WriteLine(String.Format(enUS, "{0:C}", value));
    }
}
// The example displays the following output:
//       USD 1,065.23
//       USD 19.89
//       USD -0.03
//       USD -175,902.32
//
//       USD 1,065.23
//       USD 19.89
//       USD -0.03
//       USD -175,902.32

Formatar um número de identificação nacional

Muitos números de identificação nacional consistem exclusivamente de dígitos e, portanto, podem ser facilmente formatados modificando as propriedades de um objeto NumberFormatInfo. Por exemplo, um número de seguro social nos Estados Unidos consiste em 9 dígitos organizados da seguinte forma: XXX-XX-XXXX. O exemplo a seguir pressupõe que os números do seguro social são armazenados como valores inteiros e os formata adequadamente.

using System;
using System.Globalization;

public class CustomizeSSNEx
{
    public static void Main()
    {
        // Instantiate a read-only NumberFormatInfo object.
        CultureInfo enUS = CultureInfo.CreateSpecificCulture("en-US");
        NumberFormatInfo nfi = enUS.NumberFormat;

        // Modify the relevant properties.
        nfi.NumberGroupSeparator = "-";
        nfi.NumberGroupSizes = new int[] { 3, 2, 4 };
        nfi.NumberDecimalDigits = 0;

        int[] ids = { 111223333, 999776666 };

        // Produce the result string by calling ToString.
        foreach (var id in ids)
            Console.WriteLine(id.ToString("N", enUS));

        Console.WriteLine();

        // Produce the result string using composite formatting.
        foreach (var id in ids)
            Console.WriteLine(String.Format(enUS, "{0:N}", id));
    }
}
// The example displays the following output:
//       1112-23-333
//       9997-76-666
//
//       1112-23-333
//       9997-76-666

Analisar as cadeias de caracteres numéricas

A análise envolve a conversão da representação da cadeia de caracteres de um número em um número. Cada tipo numérico no .NET inclui dois métodos de análise sobrecarregados: Parse e TryParse. O método Parse converte uma cadeia de caracteres em um número e lança uma exceção se a conversão falhar. O método TryParse converte uma cadeia de caracteres em um número, atribui o número a um argumento out e retorna um valor Boolean que indica se a conversão foi bem-sucedida.

Os métodos de análise usam, implícita ou explicitamente, um valor de enumeração NumberStyles para determinar quais elementos de estilo (como separadores de grupo, um separador decimal ou um símbolo de moeda) podem estar presentes em uma cadeia de caracteres para que a operação de análise seja bem-sucedida. Se um valor NumberStyles não for fornecido na chamada de método, o padrão será um valor NumberStyles que inclua os sinalizadores Float e AllowThousands, o que especifica que a cadeia de caracteres analisada pode incluir símbolos de grupo, um separador decimal, um sinal negativo e caracteres de espaço em branco, ou pode ser a representação da cadeia de caracteres de um número em notação exponencial.

Os métodos de análise também usam, implícita ou explicitamente, um objeto NumberFormatInfo que define os símbolos e padrões específicos que podem ocorrer na cadeia de caracteres a ser analisada. Se um objeto NumberFormatInfo não for fornecido, o padrão será o NumberFormatInfo da cultura atual. Para obter mais informações sobre análise, confira os métodos de análise individuais, como Int16.Parse(String), Int32.Parse(String, NumberStyles), Int64.Parse(String, IFormatProvider), Decimal.Parse(String, NumberStyles, IFormatProvider), Double.TryParse(String, Double) e BigInteger.TryParse(String, NumberStyles, IFormatProvider, BigInteger).

O exemplo a seguir ilustra a natureza sensível à cultura da análise de cadeias de caracteres. Ele tenta analisar uma cadeia de caracteres que inclui separadores de milhares usando as convenções das culturas en-US, fr-FR e culturas invariáveis. Uma cadeia de caracteres que inclui a vírgula como separador de grupo e o ponto como separador decimal não é analisada na cultura fr-FR, e uma cadeia de caracteres com espaço em branco como separador de grupo e uma vírgula como separador decimal não é analisada na cultura en-US e culturas invariáveis.

using System;
using System.Globalization;

public class ParseEx1
{
    public static void Main()
    {
        String[] values = { "1,034,562.91", "9 532 978,07" };
        String[] cultureNames = { "en-US", "fr-FR", "" };

        foreach (var value in values)
        {
            foreach (var cultureName in cultureNames)
            {
                CultureInfo culture = CultureInfo.CreateSpecificCulture(cultureName);
                String name = culture.Name == "" ? "Invariant" : culture.Name;
                try
                {
                    Decimal amount = Decimal.Parse(value, culture);
                    Console.WriteLine("'{0}' --> {1} ({2})", value, amount, name);
                }
                catch (FormatException)
                {
                    Console.WriteLine("'{0}': FormatException ({1})",
                                      value, name);
                }
            }
            Console.WriteLine();
        }
    }
}
// The example displays the following output:
//       '1,034,562.91' --> 1034562.91 (en-US)
//       '1,034,562.91': FormatException (fr-FR)
//       '1,034,562.91' --> 1034562.91 (Invariant)
//
//       '9 532 978,07': FormatException (en-US)
//       '9 532 978,07' --> 9532978.07 (fr-FR)
//       '9 532 978,07': FormatException (Invariant)

A análise geralmente ocorre em dois contextos:

  • Como uma operação projetada para converter a entrada do usuário em um valor numérico.

  • Como uma operação que é projetada para arredondar um valor numérico; ou seja, para desserializar um valor numérico que foi previamente serializado como uma cadeia de caracteres.

As seções a seguir discutem essas duas operações em mais detalhes.

Analisar cadeias de caracteres do usuário

Ao analisar cadeias numéricas inseridas pelo usuário, você deve sempre criar uma instância de um objeto NumberFormatInfo que reflita as configurações culturais do usuário. Para obter informações sobre como criar uma instância de um objeto NumberFormatInfo que reflita as personalizações do usuário, consulte a seção Dados dinâmicos.

O exemplo a seguir ilustra a diferença entre uma operação de análise que reflete as configurações culturais do usuário e uma que não reflete. Nesse caso, a cultura padrão do sistema é en-US, mas o usuário definiu "," como o símbolo decimal e "." como o separador de grupos no Painel de Controle, Região e Idioma. Normalmente, esses símbolos são invertidos na cultura padrão en-US. Quando o usuário digita uma cadeia de caracteres que reflete as configurações do usuário e a cadeia de caracteres é analisada por um objeto NumberFormatInfo que também reflete as configurações do usuário (substituições), a operação de análise retorna um resultado correto. No entanto, quando a cadeia de caracteres é analisada por um objeto NumberFormatInfo que reflete as configurações culturais padrão de en-US, ele confunde o símbolo de vírgula com um separador de grupo e retorna um resultado incorreto.

using System;
using System.Globalization;

public class ParseUserEx
{
    public static void Main()
    {
        CultureInfo stdCulture = CultureInfo.GetCultureInfo("en-US");
        CultureInfo custCulture = CultureInfo.CreateSpecificCulture("en-US");

        String value = "310,16";
        try
        {
            Console.WriteLine("{0} culture reflects user overrides: {1}",
                              stdCulture.Name, stdCulture.UseUserOverride);
            Decimal amount = Decimal.Parse(value, stdCulture);
            Console.WriteLine("'{0}' --> {1}", value, amount.ToString(CultureInfo.InvariantCulture));
        }
        catch (FormatException)
        {
            Console.WriteLine("Unable to parse '{0}'", value);
        }
        Console.WriteLine();

        try
        {
            Console.WriteLine("{0} culture reflects user overrides: {1}",
                              custCulture.Name, custCulture.UseUserOverride);
            Decimal amount = Decimal.Parse(value, custCulture);
            Console.WriteLine("'{0}' --> {1}", value, amount.ToString(CultureInfo.InvariantCulture));
        }
        catch (FormatException)
        {
            Console.WriteLine("Unable to parse '{0}'", value);
        }
    }
}
// The example displays the following output:
//       en-US culture reflects user overrides: False
//       '310,16' --> 31016
//
//       en-US culture reflects user overrides: True
//       '310,16' --> 310.16

Serializar e desserializar dados numéricos

Quando os dados numéricos são serializados em formato de cadeia de caracteres e posteriormente desserializados e analisados, as cadeias de caracteres devem ser geradas e analisadas usando as convenções da cultura invariável. As operações de formatação e análise nunca devem refletir as convenções de uma cultura específica. Se forem usadas configurações específicas da cultura, a portabilidade dos dados será estritamente limitada; eles só poderão ser desserializados com sucesso em um thread cujas configurações específicas da cultura sejam idênticas às do thread em que foram serializados. Em alguns casos, isso significa que os dados não podem nem mesmo ser desserializados com êxito no mesmo sistema em que foram serializados.

O exemplo a seguir ilustra o que pode acontecer quando esse princípio é violado. Os valores de ponto flutuante em uma matriz são convertidos em cadeias de caracteres quando o thread atual usa as configurações específicas da cultura en-US. Em seguida, os dados são analisados por um thread que usa as configurações específicas da cultura pt-BR. Nesse caso, embora cada operação de análise seja bem-sucedida, os dados não são transmitidos com êxito e ocorre corrupção de dados. Em outros casos, uma operação de análise pode falhar e uma exceção FormatException pode ser gerada.

using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Threading;

public class ParsePersistedEx
{
    public static void Main()
    {
        CultureInfo.CurrentCulture = CultureInfo.CreateSpecificCulture("en-US");
        PersistData();

        CultureInfo.CurrentCulture = CultureInfo.CreateSpecificCulture("pt-BR");
        RestoreData();
    }

    private static void PersistData()
    {
        // Define an array of floating-point values.
        Double[] values = { 160325.972, 8631.16, 1.304e5, 98017554.385,
                          8.5938287084321676e94 };
        Console.WriteLine("Original values: ");
        foreach (var value in values)
            Console.WriteLine(value.ToString("R", CultureInfo.InvariantCulture));

        // Serialize an array of doubles to a file
        StreamWriter sw = new StreamWriter(@".\NumericData.bin");
        for (int ctr = 0; ctr < values.Length; ctr++)
        {
            sw.Write(values[ctr].ToString("R"));
            if (ctr < values.Length - 1) sw.Write("|");
        }
        sw.Close();
        Console.WriteLine();
    }

    private static void RestoreData()
    {
        // Deserialize the data
        StreamReader sr = new StreamReader(@".\NumericData.bin");
        String data = sr.ReadToEnd();
        sr.Close();

        String[] stringValues = data.Split('|');
        List<Double> newValueList = new List<Double>();

        foreach (var stringValue in stringValues)
        {
            try
            {
                newValueList.Add(Double.Parse(stringValue));
            }
            catch (FormatException)
            {
                newValueList.Add(Double.NaN);
            }
        }

        Console.WriteLine("Restored values:");
        foreach (var newValue in newValueList)
            Console.WriteLine(newValue.ToString("R", NumberFormatInfo.InvariantInfo));
    }
}
// The example displays the following output:
//       Original values:
//       160325.972
//       8631.16
//       130400
//       98017554.385
//       8.5938287084321671E+94
//
//       Restored values:
//       160325972
//       863116
//       130400
//       98017554385
//       8.5938287084321666E+110