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


Класс NumberFormatInfo

В этой статье приводятся дополнительные замечания к справочной документации по этому API.

Класс NumberFormatInfo содержит культурно-специфическую информацию, используемую при форматировании и разборе числовых значений. Эти сведения включают символ валюты, десятичный символ, символ разделителя групп и символы для положительных и отрицательных знаков.

Создание экземпляра объекта NumberFormatInfo

Можно создать экземпляр NumberFormatInfo объекта, описывающего соглашения форматирования текущей культуры, инвариантной культуры, определенной культуры или нейтральной культуры.

Создайте экземпляр объекта NumberFormatInfo для текущей культуры

Вы можете создать экземпляр объекта NumberFormatInfo для текущей культуры любым из следующих способов. В каждом случае возвращенный NumberFormatInfo объект доступен только для чтения.

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

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

Вы можете создать объект NumberFormatInfo, в который можно записывать, который представляет соглашения текущей культуры одним из следующих способов:

  • Извлекая объект одним из способов, показанных в предыдущем примере кода, и вызывая метод Clone для возвращённого объекта NumberFormatInfo. При этом создается копия исходного NumberFormatInfo объекта, за исключением того, что его IsReadOnly свойство имеет falseзначение.

  • Вызывая CultureInfo.CreateSpecificCulture метод для создания CultureInfo объекта, представляющего текущую культуру, а затем используя его CultureInfo.NumberFormat свойство для получения NumberFormatInfo объекта.

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

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

Обратите внимание, что операционная система Windows позволяет пользователю переопределить некоторые NumberFormatInfo значения свойств, используемые в числовом форматировании и синтаксическом анализе операций с помощью элемента "Регион" и "Язык " на панели управления. Например, пользователь, язык и региональные параметры которого — английский (США), может выбрать отображение валютных значений как 1,1 ДОЛЛ. США вместо значения по умолчанию 1,1 долл. США. Объекты NumberFormatInfo, полученные описанными ранее способами, отражают переопределения этих пользователей. Если это нежелательно, вы можете создать объект NumberFormatInfo, который не отражает переопределения пользователей и является доступным для чтения и записи, а не только для чтения, вызвав конструктор CultureInfo.CultureInfo(String, Boolean) и указав значение false для аргумента useUserOverride. В следующем примере показана иллюстрация системы, текущая культура которой — английский (США) и символ валюты которого был изменен с значения по умолчанию $ на 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:    {culture.Name}");
        Console.WriteLine($"User Overrides:  {culture.UseUserOverride}");
        Console.WriteLine($"Currency Symbol: {culture.NumberFormat.CurrencySymbol}\n");

        culture = new CultureInfo(CultureInfo.CurrentCulture.Name, false);
        Console.WriteLine($"Culture Name:    {culture.Name}");
        Console.WriteLine($"User Overrides:  {culture.UseUserOverride}");
        Console.WriteLine($"Currency Symbol: {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: $

Если для свойства задано значение true, свойства CultureInfo.DateTimeFormat, CultureInfo.NumberFormat и CultureInfo.TextInfo также извлекаются из параметров пользователя. Если параметры пользователя несовместимы с культурой, связанной с CultureInfo объектом (например, если выбранный календарь не является одним из календарей, перечисленных свойством OptionalCalendars), результаты методов и значения свойств не определены.

Создайте экземпляр объекта NumberFormatInfo для инвариантной культуры

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

Вы можете создать экземпляр NumberFormatInfo объекта, представляющего соглашения о форматировании инвариантной культуры следующим образом:

В следующем примере каждый из этих методов используется для создания объекта NumberFormatInfo, представляющего инвариантную культуру. Затем он указывает, доступен ли объект только для чтения.

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

Создание экземпляра объекта NumberFormatInfo для определенного языка и региональных параметров

Определенная культура представляет язык, на котором говорят в конкретной стране или регионе. Например, en-US — это определенная культура, представляющая английский язык, который говорится в США, а en-CA — это определенная культура, представляющая английский язык, который говорится в Канаде. Вы можете создать экземпляр NumberFormatInfo объекта, представляющего соглашения о форматировании определенной культуры следующим образом:

В следующем примере используются четыре способа создания объекта NumberFormatInfo, который отражает правила форматирования культуры Индонезии. Он также указывает, доступен ли каждый объект только для чтения.

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: {nfi.IsReadOnly}");

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

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

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

Создание экземпляра объекта NumberFormatInfo для нейтральной культуры

Нейтральная культура представляет собой культуру или язык, которые не зависят от страны или региона. Обычно это родитель одного или нескольких конкретных языков и региональных параметров. Например, fr является нейтральной культурой для французского языка и родительской культурой для культуры fr-FR. Вы создаете объект NumberFormatInfo, представляющий соглашения о форматировании нейтрального языка и региональных параметров, таким же образом, как создаете объект NumberFormatInfo, представляющий соглашения о форматировании определенного языка и региональных параметров.

Тем не менее, поскольку она не зависит от конкретной страны или региона, нейтральная культура не содержит сведений о форматировании, специфичных для культуры. Вместо заполнения NumberFormatInfo объекта универсальными значениями .NET возвращает NumberFormatInfo объект, который отражает соглашения о форматировании конкретной культуры, являющейся подкатегорией нейтральной культуры. Например, объект NumberFormatInfo для нейтральной культуры отражает правила форматирования культуры en-US, а объект NumberFormatInfo для культуры fr отражает правила форматирования культуры fr-FR.

Вы можете использовать код, как показано ниже, чтобы определить, какие именно соглашения о форматировании конкретной культуры описывает каждая нейтральная культура.

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 '{name}' matches '{ci.Name}'");
                hasOneMatch = true;
            }
        }
        if (!hasOneMatch)
            Console.WriteLine($"NumberFormatInfo object for '{name}' --> No Match");

        Console.WriteLine();
    }
}

Динамические данные

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

  • Обновления системы. Культурные предпочтения, такие как символ валюты или форматы представления валюты, меняются с течением времени. В этом случае Обновление Windows включает изменения NumberFormatInfo в значение свойства для определенной культуры.

  • Культуры замены. Класс CultureAndRegionInfoBuilder можно использовать для замены данных существующей культуры.

  • Каскадные изменения значений свойств. Некоторые свойства, связанные с культурой, могут изменяться во время выполнения, что, в свою очередь, вызывает NumberFormatInfo изменение данных. Например, текущую культуру можно изменить программным способом или с помощью действия пользователя. Когда это происходит, объект, возвращаемый свойством NumberFormatInfo, изменяется на объект, связанный с текущей культурой.

  • Параметры пользователя. Пользователи вашего приложения могут изменить некоторые значения, связанные с текущей системной культурой, через параметры региона и языка на панели управления. Например, пользователи могут выбрать другой символ валюты или другой десятичный разделитель. CultureInfo.UseUserOverride Если для свойства задано true значение (значение по умолчанию), свойства NumberFormatInfo объекта также извлекаются из параметров пользователя.

Все переопределиемые пользователем свойства NumberFormatInfo объекта инициализированы при создании объекта. Существует по-прежнему вероятность несоответствия, поскольку ни создание объекта, ни процесс переопределения пользователя не являются атомарными, и соответствующие значения могут изменяться во время создания объекта. Однако эти несоответствия должны быть крайне редкими.

Вы можете контролировать, отражаются ли переопределения пользователей в NumberFormatInfo объектах, представляющих ту же культуру, что и текущая культура. В следующей таблице перечислены способы извлечения объекта с помощью NumberFormatInfo, а также указывается, отражает ли результирующий объект пользовательские переопределения.

Источник объекта CultureInfo и NumberFormatInfo Отражает изменения, внесённые пользователями
Свойство CultureInfo.CurrentCulture.NumberFormat Да
Свойство NumberFormatInfo.CurrentInfo Да
CultureInfo.CreateSpecificCultureМетод Да
CultureInfo.GetCultureInfoМетод нет
Конструктор CultureInfo(String) Да
Конструктор CultureInfo.CultureInfo(String, Boolean) Зависит от значения useUserOverride параметра

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

IFormatProvider, NumberFormatInfo и числовое форматирование

NumberFormatInfo Объект используется неявно или явно во всех числовых операциях форматирования. К ним относятся вызовы следующих методов:

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

  • Класс CultureInfo, представляющий определенную культуру (или определенный язык в конкретной стране или регионе). В операции числового форматирования метод CultureInfo.GetFormat возвращает объект NumberFormatInfo, связанный со своим свойством CultureInfo.NumberFormat.

  • Класс NumberFormatInfo, предоставляющий сведения о соглашениях форматирования соответствующей культуры. Метод NumberFormatInfo.GetFormat возвращает экземпляр самого класса.

IFormatProvider Если реализация не предоставляется методу форматирования явно, используется объект, CultureInfoCultureInfo.CurrentCulture свойством возвращаемый, представляющим текущую культуру.

В следующем примере показана связь между интерфейсом IFormatProvider и NumberFormatInfo классом в операциях форматирования путем определения пользовательской IFormatProvider реализации. Его GetFormat метод отображает имя типа объекта, запрошенного операцией форматирования. Если интерфейс запрашивает NumberFormatInfo объект, этот метод предоставляет NumberFormatInfo объект для текущего языка и региональных параметров. Как показано в выходных данных из примера, метод Decimal.ToString(IFormatProvider) запрашивает объект NumberFormatInfo для предоставления сведений о форматировании, в то время как метод String.Format(IFormatProvider, String, Object[]) запрашивает объекты NumberFormatInfo и DateTimeFormatInfo, а также реализацию ICustomFormatter.

using System;
using System.Globalization;

public class CurrentCultureFormatProvider : IFormatProvider
{
    public Object GetFormat(Type formatType)
    {
        Console.WriteLine($"Requesting an object of type {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

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

Форматирование строк и свойств NumberFormatInfo

Каждая операция форматирования использует стандартную или настраиваемую числовую строку для создания результирующих строк из числа. В некоторых случаях использование строки форматирования для создания результирующих строк явно, как показано в следующем примере. Этот код вызывает метод Decimal.ToString(IFormatProvider) для преобразования значения Decimal в разнообразные строковые представления, используя культурные соглашения 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

В других случаях строка формата используется неявно. Например, в следующем методе вызывается метод по умолчанию или без параметров, значение Decimal экземпляра отформатировано с помощью общей описатель формата ("G") и соглашений текущего языка и региональных параметровDecimal.ToString(), которые в данном случае являются 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

Каждая стандартная числовая строка использует одно или несколько NumberFormatInfo свойств для определения шаблона или символов, используемых в строке результата. Аналогичным образом, каждый описатель пользовательского числового формата, кроме "0" и "#", вставляет символы в результирующую строку, которые определяются свойствами NumberFormatInfo. В следующей таблице перечислены описатели стандартного и пользовательского числовых форматов и связанные NumberFormatInfo с ними свойства. Чтобы изменить внешний вид строки результатов для определенного языка и региональных параметров, см. раздел Модификация свойств NumberFormatInfo. Дополнительные сведения об использовании этих описателей формата см. в разделе "Стандартные числовые строки формата" и "Настраиваемые числовые строки".

Спецификатор формата Связанные свойства
"C" или "c" (спецификатор форматирования валюты) CurrencyDecimalDigits, чтобы определить число дробных цифр по умолчанию.

CurrencyDecimalSeparator, чтобы определить символ десятичного разделителя.

CurrencyGroupSeparator, чтобы определить разделитель групп или тысяч.

CurrencyGroupSizes, чтобы определить размеры целочисленных групп.

CurrencyNegativePattern, чтобы определить шаблон отрицательных валютных значений.

CurrencyPositivePattern, чтобы определить шаблон положительных валютных значений.

CurrencySymbol, чтобы определить символ валюты.

NegativeSign, чтобы определить символ отрицательного знака.
"D" или "d" (описатель десятичного формата) NegativeSign, чтобы определить символ отрицательного знака.
"E" или "e" (экспоненциальный или научный описатель формата) NegativeSign, чтобы определить символ отрицательного знака в мантиссе и экспоненте.

NumberDecimalSeparator, чтобы определить символ десятичного разделителя.

PositiveSign, чтобы определить символ положительного знака в экспоненте.
"F" или "f" (описатель формата фиксированной точки) NegativeSign, чтобы определить символ отрицательного знака.

NumberDecimalDigits, чтобы определить число дробных цифр по умолчанию.

NumberDecimalSeparator, чтобы определить символ десятичного разделителя.
"G" или "g" (общий спецификатор формата) NegativeSign, чтобы определить символ отрицательного знака.

NumberDecimalSeparator, чтобы определить символ десятичного разделителя.

PositiveSign, чтобы определить символ положительного знака для строк результатов в экспоненциальном формате.
"N" или "n" (описатель формата чисел) NegativeSign, чтобы определить символ отрицательного знака.

NumberDecimalDigits, чтобы определить число дробных цифр по умолчанию.

NumberDecimalSeparator, чтобы определить символ десятичного разделителя.

NumberGroupSeparator, чтобы определить символ, разделяющий группы (тысячи).

NumberGroupSizes, чтобы определить число целочисленных цифр в группе.

NumberNegativePattern, чтобы определить формат отрицательных значений.
"P" или "p" (описатель формата процента) NegativeSign, чтобы определить символ отрицательного знака.

PercentDecimalDigits, чтобы определить число дробных цифр по умолчанию.

PercentDecimalSeparator, чтобы определить символ десятичного разделителя.

PercentGroupSeparator, чтобы определить символ разделителя групп.

PercentGroupSizes, чтобы определить число целочисленных цифр в группе.

PercentNegativePattern, чтобы определить размещение символа процента и отрицательного символа для отрицательных значений.

PercentPositivePattern, чтобы определить размещение символа процента для положительных значений.

PercentSymbol, чтобы определить символ процента.
"R" или "r" (описатель формата кругового пути) NegativeSign, чтобы определить символ отрицательного знака.

NumberDecimalSeparator, чтобы определить символ десятичного разделителя.

PositiveSign, чтобы определить символ положительного знака в экспоненте.
"X" или "x" (описатель шестнадцатеричного формата) Нет.
"." (описатель пользовательского формата десятичной запятой) NumberDecimalSeparator, чтобы определить символ десятичного разделителя.
"," (описатель пользовательского формата разделителя групп) NumberGroupSeparator, чтобы определить символ разделителя группы (тысячи).
"%" (пользовательский спецификатор формата для представления процентов) PercentSymbol, чтобы определить символ процента.
""." (на милле заполнитель настраиваемого описатель формата) PerMilleSymbol, чтобы определить символ промилле.
"E" (описатель пользовательского формата экспоненциальной нотации) NegativeSign, чтобы определить символ отрицательного знака в мантиссе и экспоненте.

PositiveSign, чтобы определить символ положительного знака в экспоненте.

Обратите внимание, что класс NumberFormatInfo содержит свойство NativeDigits, которое указывает десятеричные цифры, используемые конкретной культурой. Однако свойство не используется в операциях форматирования; В результирующем строке используются только цифры basic Latin 0 (U+0030) –9 (U+0039). Кроме того, для Single и значений NaN, PositiveInfinityа DoubleNegativeInfinityтакже строка результатов состоит исключительно из символов, определенных NaNSymbolPositiveInfinitySymbolNegativeInfinitySymbol и свойствами соответственно.

Изменение свойств NumberFormatInfo

Вы можете изменить свойства NumberFormatInfo объекта, чтобы настроить строку результатов, созданную в операции числового форматирования. Для этого выполните указанные ниже действия.

  1. Создайте копию объекта NumberFormatInfo с возможностью чтения и записи, соглашения о форматировании которого необходимо изменить. Дополнительные сведения см. в разделе "Создание экземпляра объекта NumberFormatInfo ".

  2. Измените свойство или свойства, используемые для создания требуемой строки результата. Сведения о том, как методы форматирования используют NumberFormatInfo свойства для определения строк результатов, см. в разделе свойств Format и NumberFormatInfo .

  3. Используйте пользовательский NumberFormatInfo объект в качестве аргумента IFormatProvider в вызовах методов форматирования.

Замечание

Вместо того чтобы динамически изменять значения свойств культуры при каждом запуске приложения, можно использовать класс CultureAndRegionInfoBuilder для определения пользовательской культуры (культура с уникальным именем, которая дополняет существующие культуры) или замещающей культуры (которую используют вместо определенной культуры).

В следующих разделах приведены некоторые примеры.

Изменение символа и шаблона валюты

В следующем примере модифицируется объект NumberFormatInfo, который представляет соглашения о форматировании для культуры en-US. Он назначает символ CurrencySymbol валюты ISO-4217 свойству и определяет шаблон для значений валют, состоящих из символа валюты, за которым следует пробел и числовое значение.

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

Форматировать национальный идентификационный номер

Многие национальные идентификационные номера состоят исключительно из цифр, поэтому их можно легко отформатировать, изменив свойства NumberFormatInfo объекта. Например, номер социального страхования в Соединенных Штатах состоит из 9 цифр, расположенных следующим образом: XXX-XX-XXXX В следующем примере предполагается, что номера социального страхования хранятся в виде целых значений и форматируют их соответствующим образом.

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

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

Синтаксический анализ включает преобразование строкового представления числа в число. Каждый числовой тип в .NET включает два перегруженных метода синтаксического анализа: Parse и TryParse. Метод Parse преобразует строку в число и создает исключение, если преобразование завершается ошибкой. Метод TryParse преобразует строку в число, назначает число out аргументу и возвращает Boolean значение, указывающее, успешно ли выполнено преобразование.

Методы синтаксического анализа неявно или явно используют NumberStyles значение перечисления, чтобы определить, какие элементы стиля (например, разделители групп, десятичный разделитель или символ валюты) могут присутствовать в строке, если операция синтаксического анализа будет выполнена успешно. NumberStyles Если значение не указано в вызове метода, значение по умолчанию — это NumberStyles значение, включающее Float и AllowThousands флаги, указывающее, что синтаксическая строка может включать символы групп, десятичный разделитель, отрицательный знак и символы пробела, или это может быть строковое представление числа в экспоненциальной нотации.

Методы синтаксического анализа также неявно или явно используют NumberFormatInfo объект, определяющий определенные символы и шаблоны, которые могут возникать в строке для анализа. NumberFormatInfo Если объект не указан, по умолчанию используется NumberFormatInfo для текущей культуры. Дополнительные сведения о синтаксическом анализе см. в отдельных методах синтаксического анализа, таких как Int16.Parse(String), Int32.Parse(String, NumberStyles), Int64.Parse(String, IFormatProvider), Decimal.Parse(String, NumberStyles, IFormatProvider)Double.TryParse(String, Double)и BigInteger.TryParse(String, NumberStyles, IFormatProvider, BigInteger).

В следующем примере показан чувствительный к культурным особенностям характер анализа строк. Он пытается проанализировать строку, включающую тысячи разделителей с помощью соглашений en-US, fr-FRи инвариантных региональных параметров. Строка, содержащая запятую в качестве разделителя групп и точку в качестве десятичного разделителя, не может быть распознана в культурных настройках fr-FR, а строка с пробелом в качестве разделителя групп и запятой в качестве десятичного разделителя не может быть распознана в культурных настройках en-US и инвариантной культуре.

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($"'{value}' --> {amount} ({name})");
                }
                catch (FormatException)
                {
                    Console.WriteLine($"'{value}': FormatException ({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)

Анализ обычно происходит в двух контекстах:

  • Как операция, предназначенная для преобразования входных данных пользователей в числовое значение.

  • Операция, направленная на возврат числового значения в исходный формат; то есть десериализация числового значения, который ранее был сериализован в формате строки.

В следующих разделах подробно рассматриваются эти две операции.

Анализ строк пользователей

При синтаксическом анализе числовых строк, входных пользователем, всегда следует создать экземпляр NumberFormatInfo объекта, который отражает культурные параметры пользователя. Сведения о создании экземпляра NumberFormatInfo объекта, который отражает настройки пользователей, см. в разделе динамических данных .

В следующем примере показано различие между операцией синтаксического анализа, которая отражает культурные настройки пользователя, и той, которая их не отражает. В этом случае системный язык и региональные параметры по умолчанию en-US, но пользователь определил "", как десятичный символ и "." в качестве разделителя групп на панели управления, регионе и языке. Обычно эти символы являются обратными в языковой и региональной культуре en-US по умолчанию. Когда пользователь вводит строку, которая отражает параметры пользователя, и строка анализируется объектом, который также отражает параметры пользователя (переопределяет), операция синтаксического NumberFormatInfo анализа возвращает правильный результат. Однако если строка анализируется NumberFormatInfo объектом, который соответствует стандартным en-US культурным настройкам, он принимает символ запятой за разделитель групп и дает неверный результат.

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($"{stdCulture.Name} culture reflects user overrides: {stdCulture.UseUserOverride}");
            Decimal amount = Decimal.Parse(value, stdCulture);
            Console.WriteLine($"'{value}' --> {amount.ToString(CultureInfo.InvariantCulture)}");
        }
        catch (FormatException)
        {
            Console.WriteLine($"Unable to parse '{value}'");
        }
        Console.WriteLine();

        try
        {
            Console.WriteLine($"{custCulture.Name} culture reflects user overrides: {custCulture.UseUserOverride}");
            Decimal amount = Decimal.Parse(value, custCulture);
            Console.WriteLine($"'{value}' --> {amount.ToString(CultureInfo.InvariantCulture)}");
        }
        catch (FormatException)
        {
            Console.WriteLine($"Unable to parse '{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

Сериализация и десериализация числовых данных

При сериализации числовых данных в строковом формате и последующем десериализации и разборе строки следует создавать и анализировать с помощью правил инвариантной культуры. Операции форматирования и синтаксического анализа никогда не должны отражать соглашения конкретной культуры. Если используются культурно-специфические настройки, переносимость данных строго ограничена; успешно десериализировать данные можно только в потоке, культурно-специфические настройки которого идентичны настройкам потока, в котором они были сериализованы. В некоторых случаях это означает, что данные даже не могут быть успешно десериализированы в той же системе, в которой она была сериализована.

В следующем примере показано, что может произойти при нарушении этого принципа. Значения с плавающей запятой в массиве преобразуются в строки, когда текущий поток использует параметры, специфичные для культуры en-US. Затем данные анализируются потоком, использующими параметры языка и региональных параметров pt-BR языка и региональных параметров. В этом случае, хотя каждая операция синтаксического анализа завершается успешно, данные не успешно выполняются и возникают повреждения данных. В других случаях операция синтаксического анализа может завершиться ошибкой, и FormatException может быть выброшено исключение.

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