.NET 复合格式设置功能采用对象列表和复合格式字符串作为输入。 复合格式字符串由与索引占位符混合的固定文本组成,称为格式项。 这些格式项对应于列表中的对象。 格式设置操作产生的结果字符串由原始固定文本和列表中对象的字符串表示形式混和组成。
重要
如果使用的语言及其版本支持它们,则可以使用 内插字符串 ,而不是使用复合格式字符串。 内插字符串包含 内插表达式。 每个内插表达式都使用表达式的值进行解析,并在分配字符串时包含在结果字符串中。 有关详细信息,请参阅字符串内插(C# 参考)和内插字符串(Visual Basic 参考)。
以下方法支持复合格式设置功能:
- String.Format,返回格式化的结果字符串。
- StringBuilder.AppendFormat,它将格式化的结果字符串追加到对象 StringBuilder 。
- Console.WriteLine 方法的某些重载,它将格式化的结果字符串显示到控制台上。
- TextWriter.WriteLine 方法的某些重载,它将格式化的结果字符串写入流或文件中。 派生自 TextWriter的类(如 StreamWriter 和 HtmlTextWriter)也共享此功能。
- Debug.WriteLine(String, Object[]),它将格式化消息输出到跟踪侦听器。
- Trace.TraceError(String, Object[])、Trace.TraceInformation(String, Object[]) 和 Trace.TraceWarning(String, Object[]) 方法,它们将格式化消息输出到跟踪侦听器。
- TraceSource.TraceInformation(String, Object[]) 方法,它将信息性方法写入跟踪侦听器中。
复合格式字符串
复合格式字符串和对象列表用作支持复合格式设置功能的方法的参数。 复合格式字符串由零个或多个固定文本以及一个或多个格式项组成。 固定文本是所选择的任何字符串,并且每个格式项对应于列表中的一个对象或装箱的结构。 每个对象的字符串表示形式将替换相应的格式项。
请考虑以下 Format 代码片段:
string.Format("Name = {0}, hours = {1:hh}", "Fred", DateTime.Now);
String.Format("Name = {0}, hours = {1:hh}", "Fred", DateTime.Now)
固定文本为 Name =
和 , hours =
。 格式项是 {0}
,其索引为 0 对应于对象 name
, {1:hh}
其索引为 1 对应于对象 DateTime.Now
。
格式项语法
每个格式项都采用下面的形式并包含以下组件:
{index[,width][:formatString]}
必须使用成对的大括号({
和 }
)。
索引组件
强制 index
组件(也称为参数说明符)是从 0 开始的数字,用于标识对象列表中的对应项。 也就是说,参数说明符为 0
的格式项对列表中的第一个对象进行格式化。 格式项的参数说明符为 1
,用于格式化列表中的第二个对象,依此类推。 以下示例包含四个参数说明符(编号为零到 3)来表示小于 10 的质数:
string primes = string.Format("Four prime numbers: {0}, {1}, {2}, {3}",
2, 3, 5, 7);
Console.WriteLine(primes);
// The example displays the following output:
// Four prime numbers: 2, 3, 5, 7
Dim primes As String = String.Format("Four prime numbers: {0}, {1}, {2}, {3}",
2, 3, 5, 7)
Console.WriteLine(primes)
'The example displays the following output
' Four prime numbers 2, 3, 5, 7
通过指定相同的参数说明符,多个格式项可以引用对象列表中的同一元素。 例如,可以通过指定复合格式字符串(如 "0x{0:X} {0:E} {0:N}"
以下示例所示)以十六进制、科学和数字格式设置相同的数值格式:
string multiple = string.Format("0x{0:X} {0:E} {0:N}",
Int64.MaxValue);
Console.WriteLine(multiple);
// The example displays the following output:
// 0x7FFFFFFFFFFFFFFF 9.223372E+018 9,223,372,036,854,775,807.00
Dim multiple As String = String.Format("0x{0:X} {0:E} {0:N}",
Int64.MaxValue)
Console.WriteLine(multiple)
'The example displays the following output
' 0x7FFFFFFFFFFFFFFF 9.223372E+018 9,223,372,036,854,775,807.00
每个格式项都可以引用列表中的任何对象。 例如,如果有三个对象,可以通过指定复合格式字符串(例如 {1} {0} {2}
)设置第二个、第一个和第三个对象的格式。 将忽略格式项未引用的对象。 如果参数说明符指定对象列表边界外的项,则会在运行时引发 A FormatException 。
宽度组件
可选 width
组件是一个有符号整数,指示首选格式化字段宽度。 如果该值 width
小于格式化字符串的长度, width
则将被忽略,并且格式化字符串的长度用作字段宽度。 如果 width
为正值,则字段中的格式化数据为右对齐,如果 width
为负,则为左对齐。 如果需要填充,则使用空白。 如果 width
指定了逗号,则需要逗号。
以下示例定义了两个数组,一个数组包含员工的姓名,另一个数组包含两周内工作的时间。 复合格式字符串左对齐 20 个字符字段中的名称,并在 5 个字符的字段中右对齐其小时数。 “N1”标准格式字符串设置带有小数位的小时数格式。
string[] names = { "Adam", "Bridgette", "Carla", "Daniel",
"Ebenezer", "Francine", "George" };
decimal[] hours = { 40, 6.667m, 40.39m, 82,
40.333m, 80, 16.75m };
Console.WriteLine("{0,-20} {1,5}\n", "Name", "Hours");
for (int counter = 0; counter < names.Length; counter++)
Console.WriteLine("{0,-20} {1,5:N1}", names[counter], hours[counter]);
// The example displays the following output:
// Name Hours
//
// Adam 40.0
// Bridgette 6.7
// Carla 40.4
// Daniel 82.0
// Ebenezer 40.3
// Francine 80.0
// George 16.8
Dim names As String() = {"Adam", "Bridgette", "Carla", "Daniel",
"Ebenezer", "Francine", "George"}
Dim hours As Decimal() = {40, 6.667D, 40.39D, 82,
40.333D, 80, 16.75D}
Console.WriteLine("{0,-20} {1,5}\n", "Name", "Hours")
For counter = 0 To names.Length - 1
Console.WriteLine("{0,-20} {1,5:N1}", names(counter), hours(counter))
Next
'The example displays the following output
' Name Hours
'
' Adam 40.0
' Bridgette 6.7
' Carla 40.4
' Daniel 82.0
' Ebenezer 40.3
' Francine 80.0
' George 16.8
格式字符串组件
可选 formatString
组件是一个格式字符串,适用于要格式化的对象的类型。 可以指定:
如果未指定 formatString
,则会使用数字、日期时间或枚举类型的常规格式说明符 (“G”)。 如果指定 formatString
,则需要使用冒号。
下表列出了支持预定义格式字符串集的 .NET 类库中的类型或类别,并提供指向列出受支持格式字符串的文章的链接。 字符串格式是一种可扩展的机制,可用于为所有现有类型定义新的格式字符串,并定义应用程序定义类型支持的一组格式字符串。
有关详细信息,请参阅 IFormattable 和 ICustomFormatter 接口文章。
类型或类型类别 | 请参阅 |
---|---|
日期和时间类型 (DateTime, DateTimeOffset) | 标准日期和时间格式字符串 自定义日期和时间格式字符串 |
枚举类型(派生自 System.Enum的所有类型) | 枚举格式字符串 |
数值类型(BigInteger、Byte、Decimal、Double、Int16、Int32、Int64、SByte、Single、UInt16、UInt32、UInt64) | 标准数字格式字符串 自定义数字格式字符串 |
Guid | Guid.ToString(String) |
TimeSpan | 标准 TimeSpan 格式字符串 自定义 TimeSpan 格式字符串 |
转义大括号
大括号被视为格式项的起始和结束标记。 若要显示文本左大括号或右大括号,必须使用转义序列。 在固定文本中指定两个左大括号({{
)以显示一个左大括号({
)或两个右大括号(}}
)以显示一个右大括号(}
)。
具有格式项的转义大括号在 .NET 和 .NET Framework 之间以不同的方式进行分析。
.NET
大括号可以围绕格式项进行转义。 例如,考虑格式项 {{{0:D}}}
,它旨在显示左大括号、格式化为十进制数字的数值和右大括号。 格式项按以下方式进行解释:
- 前两个左大括号 (
{{
) 被转义,生成一个左大括号。 - 接下来的三个字符(
{0:
)解释为格式项的开头。 - 下一个字符 (
D
) 解释为十进制标准数字格式说明符。 - 下一个大括号 (
}
) 被解释为格式项的末尾。 - 最后两个右大括号被转义,并生成一个右大括号。
- 显示的最终结果是文本字符串。
{6324}
int value = 6324;
string output = string.Format("{{{0:D}}}", value);
Console.WriteLine(output);
// The example displays the following output:
// {6324}
Dim value As Integer = 6324
Dim output As String = String.Format("{{{0:D}}}", value)
Console.WriteLine(output)
'The example displays the following output
' {6324}
.NET Framework
按照在格式项中遇到大括号的顺序依次解释它们。 不支持解释嵌套的大括号。
解释转义大括号的方式会导致意外的结果。 例如,考虑格式项 {{{0:D}}}
,它旨在显示左大括号、格式化为十进制数字的数值和右大括号。 但是,格式项按以下方式进行解释:
- 前两个左大括号 (
{{
) 被转义,生成一个左大括号。 - 接下来的三个字符(
{0:
)解释为格式项的开头。 - 下一个字符 (
D
) 将被解释为 Decimal 标准数值格式说明符,但后面的两个转义大括号 (}}
) 生成单个大括号。 由于生成的字符串 (D}
) 不是标准数值格式说明符,因此生成的字符串被解释为一个自定义格式字符串,表示显示文本字符串D}
。 - 最后一个大括号 (
}
) 被解释为格式项的末尾。 - 显示的最终结果是文本字符串。
{D}
不会显示本来要设置格式的数值。
int value = 6324;
string output = string.Format("{{{0:D}}}",
value);
Console.WriteLine(output);
// The example displays the following output:
// {D}
Dim value As Integer = 6324
Dim output As String = String.Format("{{{0:D}}}",
value)
Console.WriteLine(output)
'The example displays the following output:
' {D}
在编写代码时,避免错误解释转义大括号和格式项的一种方法是单独设置大括号和格式项的格式。 也就是说,在第一个格式操作中显示文本左大括号。 在下一操作中显示格式项的结果,然后在最后一个操作中显示文本右大括号。 以下示例演示了此方法:
int value = 6324;
string output = string.Format("{0}{1:D}{2}",
"{", value, "}");
Console.WriteLine(output);
// The example displays the following output:
// {6324}
Dim value As Integer = 6324
Dim output As String = String.Format("{0}{1:D}{2}",
"{", value, "}")
Console.WriteLine(output)
'The example displays the following output:
' {6324}
处理顺序
如果对复合格式设置方法的调用包含 IFormatProvider 其值不是 null
的参数,运行时将调用其 IFormatProvider.GetFormat 方法以请求 ICustomFormatter 实现。 如果该方法可以返回 ICustomFormatter 实现,则会在调用复合格式方法期间缓存该实现。
与格式项对应的参数列表中的每个值都转换为字符串,如下所示:
如果要设置格式的值是
null
,则返回空字符串String.Empty。ICustomFormatter如果实现可用,运行时将调用其Format方法。 运行时将格式项的
formatString
值(如果不存在则为null
)传递给方法。 运行时还会将 IFormatProvider 实现传递给方法。 如果调用ICustomFormatter.Format方法返回null
,则继续执行下一步。 否则,将返回调用的结果 ICustomFormatter.Format 。如果值实现 IFormattable 接口,将调用接口 ToString(String, IFormatProvider) 的方法。 如果格式项中存在一个值,则
formatString
该值将传递给该方法。 否则,传递null
。 参数 IFormatProvider 确定如下:对于数值,如果调用具有非 null IFormatProvider 参数的复合格式设置方法,运行时会从其IFormatProvider.GetFormat方法请求对象NumberFormatInfo。 在以下情况下,使用当前区域性的 NumberFormatInfo 对象:无法提供该值、实际参数值为
null
或复合格式设置方法没有 IFormatProvider 形式参数。对于日期和时间值,如果调用具有非 null IFormatProvider 参数的复合格式设置方法,运行时会从其IFormatProvider.GetFormat方法请求对象DateTimeFormatInfo。 在以下情况下,改为使用当前区域性的 DateTimeFormatInfo 对象:
- 该方法 IFormatProvider.GetFormat 无法提供 DateTimeFormatInfo 对象。
- 参数的值为
null
. - 复合格式设置方法没有参数 IFormatProvider 。
对于其他类型的对象,如果调用带 IFormatProvider 参数的复合格式设置方法,它的值会直接传递到 IFormattable.ToString 实现。 否则,
null
被传递给 IFormattable.ToString 的实现。
调用类型的无参数的
ToString
方法(该方法将重写 Object.ToString() 或继承其基类的行为)。 在这种情况下,将忽略由组件在格式项中指定的格式字符串formatString
(如果存在)。
前面的步骤执行完毕之后应用对齐。
代码示例
以下示例演示一个使用复合格式创建的字符串,另一个字符串使用对象的 ToString
方法创建。 这两种类型的格式都会产生等效的结果。
string formatString1 = string.Format("{0:dddd MMMM}", DateTime.Now);
string formatString2 = DateTime.Now.ToString("dddd MMMM");
Dim formatString1 As String = String.Format("{0:dddd MMMM}", DateTime.Now)
Dim formatString2 As String = DateTime.Now.ToString("dddd MMMM")
假设当前日期是 5 月的一个星期四,则在上述示例中,这两个字符串的值在美国英语文化中均为 Thursday May
。
Console.WriteLine 公开的功能与 String.Format 相同。 两种方法之间的唯一区别是 String.Format ,将结果作为字符串返回,同时 Console.WriteLine 将结果写入与 Console 对象关联的输出流。 以下示例使用 Console.WriteLine 方法将 myNumber
的值格式化为货币值:
int myNumber = 100;
Console.WriteLine($"{myNumber:C}");
// The example displays the following output
// if en-US is the current culture:
// $100.00
Dim myNumber As Integer = 100
Console.WriteLine("{0:C}", myNumber)
'The example displays the following output
'if en-US Is the current culture:
' $100.00
以下示例演示如何设置多个对象的格式,包括采用两种不同的方式设置一个对象的格式:
string myName = "Fred";
Console.WriteLine(string.Format("Name = {0}, hours = {1:hh}, minutes = {1:mm}",
myName, DateTime.Now));
// Depending on the current time, the example displays output like the following:
// Name = Fred, hours = 11, minutes = 30
Dim myName As String = "Fred"
Console.WriteLine(String.Format("Name = {0}, hours = {1:hh}, minutes = {1:mm}",
myName, DateTime.Now))
'Depending on the current time, the example displays output Like the following:
' Name = Fred, hours = 11, minutes = 30
下面的示例演示如何在格式设置中使用宽度。 设置了格式的自变量放置在竖线字符 (|
) 之间以突出显示得到的对齐。
string firstName = "Fred";
string lastName = "Opals";
int myNumber = 100;
string formatFirstName = string.Format("First Name = |{0,10}|", firstName);
string formatLastName = string.Format("Last Name = |{0,10}|", lastName);
string formatPrice = string.Format("Price = |{0,10:C}|", myNumber);
Console.WriteLine(formatFirstName);
Console.WriteLine(formatLastName);
Console.WriteLine(formatPrice);
Console.WriteLine();
formatFirstName = string.Format("First Name = |{0,-10}|", firstName);
formatLastName = string.Format("Last Name = |{0,-10}|", lastName);
formatPrice = string.Format("Price = |{0,-10:C}|", myNumber);
Console.WriteLine(formatFirstName);
Console.WriteLine(formatLastName);
Console.WriteLine(formatPrice);
// The example displays the following output on a system whose current
// culture is en-US:
// First Name = | Fred|
// Last Name = | Opals|
// Price = | $100.00|
//
// First Name = |Fred |
// Last Name = |Opals |
// Price = |$100.00 |
Dim firstName As String = "Fred"
Dim lastName As String = "Opals"
Dim myNumber As Integer = 100
Dim formatFirstName As String = String.Format("First Name = |{0,10}|", firstName)
Dim formatLastName As String = String.Format("Last Name = |{0,10}|", lastName)
Dim formatPrice As String = String.Format("Price = |{0,10:C}|", myNumber)
Console.WriteLine(formatFirstName)
Console.WriteLine(formatLastName)
Console.WriteLine(formatPrice)
Console.WriteLine()
formatFirstName = String.Format("First Name = |{0,-10}|", firstName)
formatLastName = String.Format("Last Name = |{0,-10}|", lastName)
formatPrice = String.Format("Price = |{0,-10:C}|", myNumber)
Console.WriteLine(formatFirstName)
Console.WriteLine(formatLastName)
Console.WriteLine(formatPrice)
'The example displays the following output on a system whose current
'culture Is en-US:
' First Name = | Fred|
' Last Name = | Opals|
' Price = | $100.00|
'
' First Name = |Fred |
' Last Name = |Opals |
' Price = |$100.00 |