本文提供了此 API 参考文档的补充说明。
NumberFormatInfo 类包含格式化和解析数值时使用的区域性特定的信息。 此信息包括货币符号、小数符号、组分隔符和正负符号的符号。
实例化 NumberFormatInfo 对象
可以实例化代表当前区域性、固定区域性、特定区域性或非特定区域性的格式化约定的 NumberFormatInfo 对象。
实例化当前区域性的 NumberFormatInfo 对象
可以通过以下任一方式实例化当前区域性的 NumberFormatInfo 对象。 在每种情况下,返回 NumberFormatInfo 的对象都是只读的。
通过从 CultureInfo.CurrentCulture 属性检索代表当前区域性的CultureInfo 对象,并从其 CultureInfo.NumberFormat 属性检索 NumberFormatInfo 对象。
通过检索
static
(在 Visual Basic 中为Shared
)CurrentInfo 属性返回的 NumberFormatInfo 对象。通过使用代表当前区域性的 CultureInfo 对象调用 GetInstance 方法。
以下示例使用这三种方法来创建表示当前文化格式规范的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 对象,该对象使用以下任一方式表示当前文化的约定。
通过在之前代码示例中所示的任何一种方式检索NumberFormatInfo对象,并在返回的NumberFormatInfo对象上调用Clone方法。 这会创建原始 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操作系统允许用户通过控制面板中的“区域和语言”项,覆盖数字格式设置和分析操作中使用的某些属性值。 例如,区域性为英语(美国)的用户可能会选择将货币值显示为 1.1 美元,而不是默认值 $1.1。 以前文讨论的方式检索的 NumberFormatInfo 对象都反映了这些用户替代。 如果这是不可取的,您可以通过调用CultureInfo.CultureInfo(String, Boolean)构造函数并为useUserOverride
参数提供false
值,创建一个NumberFormatInfo对象,该对象不反映用户覆盖设置,并且是读写而不是只读的。 以下示例描述了当前区域性为英语(美国)且货币符号已从默认的 $ 更改为 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: $
如果将CultureInfo.UseUserOverride属性设置为true
,则属性CultureInfo.DateTimeFormat、CultureInfo.NumberFormat和CultureInfo.TextInfo也将从用户设置中检索。 如果用户设置与 CultureInfo 对象所关联的文化信息不兼容(例如,如果所选的日历不在 OptionalCalendars 属性列出的日历中),那么方法的结果和属性的值就是未定义的。
实例化固定区域性的 NumberFormatInfo 对象
固定区域性代表对区域性不敏感的区域性。 它基于英语,但不基于任何特定的英语国家/地区。 尽管特定区域性的数据可以是动态的,并且可以更改以反映新的文化约定或用户首选项,但固定区域性的数据不会更改。 代表固定区域性的格式化约定的 NumberFormatInfo 对象可用于结果字符串不应因区域性而异的格式化操作。
可以通过以下方式实例化代表固定区域性格式化约定的 NumberFormatInfo 对象:
通过检索 InvariantInfo 属性的值。 返回 NumberFormatInfo 的对象是只读的。
通过从CultureInfo.InvariantCulture属性返回的CultureInfo对象中检索CultureInfo.NumberFormat的值。 返回 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 对象:
通过调用 CultureInfo.GetCultureInfo(String) 该方法并检索返回 CultureInfo 对象的 NumberFormat 属性的值。 返回 NumberFormatInfo 的对象是只读的。
通过向静态 CultureInfo 方法传递一个 NumberFormatInfo 对象,该对象代表要检索其 GetInstance 对象的区域性。 返回 NumberFormatInfo 的对象是读/写。
通过调用 CultureInfo.CreateSpecificCulture 该方法并检索返回 CultureInfo 对象的 NumberFormat 属性的值。 返回 NumberFormatInfo 的对象是读/写。
通过调用其中一个 CultureInfo.CultureInfo 类构造函数并检索返回 CultureInfo 对象的 NumberFormat 属性的值。 返回 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 特定区域性的格式约定的对象相同。
但是,由于它独立于特定国家/地区,因此中性文化缺乏特定于区域性的格式信息。 .NET 不会用通用值填充 NumberFormatInfo 对象,而是返回一个 NumberFormatInfo 对象,该对象反映了一种特定区域性的格式约定,这种区域性是中性区域性的一个子部分。 例如, NumberFormatInfo 中性 en 区域性的对象反映 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 类提供的区域性数据类似。 不应对与特定 CultureInfo 对象关联的 NumberFormatInfo 对象值的稳定性做出任何假设。 只有固定区域性及其关联的 NumberFormatInfo 对象提供的数据是稳定的。 出于以下原因,其他数据可以在应用程序会话之间更改,甚至可以在单个会话中更改:
系统更新。 文化首选项,如货币符号或货币格式随时间变化。 发生这种情况时,Windows Update 会包含对特定区域性的 NumberFormatInfo 属性值的更改。
替代文化。 CultureAndRegionInfoBuilder 类可用于替换现有区域性的数据。
对属性值的级联更改。 许多与文化相关的属性可以在运行时更改,从而导致 NumberFormatInfo 数据发生更改。 例如,可以通过编程方式或用户操作来更改当前文化设置。 发生这种情况时,CurrentInfo 属性返回的 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 对象在所有数字格式化操作中隐式或显式使用。 其中包括对以下方法的调用:
所有数值格式设置方法,例如 Int32.ToString, Double.ToString和 Convert.ToString(Int32)。
主要的复合格式设置方法。 String.Format
其他复合格式设置方法,例如 Console.WriteLine(String, Object[]) 和 StringBuilder.AppendFormat(String, Object[])。
所有数字格式化操作均使用 IFormatProvider 实施。 该 IFormatProvider 接口包括单个方法 GetFormat(Type)。 这是一个回调方法,接收一个Type对象,该对象表示需要提供格式信息的类型。 该方法负责返回该类型的实例,或者 null
,如果它无法提供该类型的实例。 .NET 提供了两种 IFormatProvider 用于格式化数字的实现:
该 CultureInfo 类表示特定文化(或特定国家/地区中的特定语言)。 在数值格式设置作中,该方法 CultureInfo.GetFormat 返回 NumberFormatInfo 与其 CultureInfo.NumberFormat 属性关联的对象。
NumberFormatInfo 类,提供有关其关联区域性的格式化约定的信息。 该方法 NumberFormatInfo.GetFormat 返回本身的实例。
如果未向格式化方法显式提供 IFormatProvider 实施,则使用由 CultureInfo.CurrentCulture 属性返回的表示当前区域性的 CultureInfo 对象。
以下示例通过定义自定义IFormatProvider实现,说明IFormatProvider接口与格式化操作中的NumberFormatInfo类之间的关系。 其 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) 方法,使用 en-US 文化的格式约定,将 Decimal 值转换为多种不同的字符串表示形式。
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.ToString() 参数方法的以下方法调用中,实例的值 Decimal 使用常规(“G”)格式说明符和当前区域性的约定进行格式化,在本例中为 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 属性,该属性指定特定区域性使用的基 10 位数字。 但是,属性不用于格式设置作;结果字符串中仅使用基本拉丁数字 0(U+0030) 到 9(U+0039)。 此外,对于 NaN
、PositiveInfinity
和 NegativeInfinity
的 Single 和 Double 值,结果字符串仅由 NaNSymbol、PositiveInfinitySymbol 和 NegativeInfinitySymbol 属性分别定义的符号组成。
修改 NumberFormatInfo 属性
通过修改 NumberFormatInfo 对象的属性,您可以自定义数值格式操作中生成的结果字符串。 为此,请执行以下步骤:
创建一个对象的 NumberFormatInfo 读/写副本,以便可以修改其格式设置约定。 有关详细信息,请参阅 “实例化 NumberFormatInfo 对象 ”部分。
修改用于生成所需结果字符串的属性或属性。 有关如何使用 NumberFormatInfo 属性定义结果字符串的格式设置方法的信息,请参阅 Format strings 和 NumberFormatInfo 属性 部分。
在调用格式化方法时,将自定义 NumberFormatInfo 对象用作 IFormatProvider 参数。
注释
与其在每次启动应用程序时动态修改区域性的属性值,不如使用 CultureAndRegionInfoBuilder 类来定义自定义区域性(具有唯一名称并补充现有区域性的文化)或替代区域性(用于替代特定区域性的文化)。
以下部分提供了一些示例。
修改货币符号和模式
以下示例修改代表 en-US 区域性格式化约定的 NumberFormatInfo 对象。 它将 ISO-4217 货币符号 CurrencySymbol 分配给属性,并定义货币值的模式,该模式由货币符号后跟空格和数值组成。
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 对象进行解析时,解析操作会返回正确的结果。 但是,当反映标准 en-US 区域性设置的 NumberFormatInfo 对象解析字符串时,它会将逗号误认为组分隔符,并返回错误的结果。
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