# System.String.Format 方法

## 開始使用 String.Format 方法

Decimal pricePerOunce = 17.36m;
String s = String.Format("The current price is {0} per ounce.",
pricePerOunce);
Console.WriteLine(s);
// Result: The current price is 17.36 per ounce.

let pricePerOunce = 17.36m
String.Format("The current price is {0} per ounce.", pricePerOunce)
|> printfn "%s"
// Result: The current price is 17.36 per ounce.

Dim pricePerOunce As Decimal = 17.36D
Dim s As String = String.Format("The current price is {0} per ounce.",
pricePerOunce)
' Result: The current price is 17.36 per ounce.


Decimal pricePerOunce = 17.36m;
String s = String.Format("The current price is {0:C2} per ounce.",
pricePerOunce);
Console.WriteLine(s);
// Result if current culture is en-US:
//      The current price is $17.36 per ounce.  let pricePerOunce = 17.36m String.Format("The current price is {0:C2} per ounce.", pricePerOunce) |> printfn "%s" // Result if current culture is en-US: // The current price is$17.36 per ounce.

Dim pricePerOunce As Decimal = 17.36D
Dim s As String = String.Format("The current price is {0:C2} per ounce.",
pricePerOunce)
' Result if current culture is en-US:
'      The current price is $17.36 per ounce.  除了格式化之外，您也可以控制對齊和間距。 ### 插入字串 String.Format 會以格式字串開頭，後面接著一或多個物件或表達式，這些物件或表達式將會轉換成字元串，並在格式字串中指定的位置插入。 例如： decimal temp = 20.4m; string s = String.Format("The temperature is {0}°C.", temp); Console.WriteLine(s); // Displays 'The temperature is 20.4°C.'  let temp = 20.4m String.Format("The temperature is {0}°C.", temp) |> printfn "%s" // Displays 'The temperature is 20.4°C.'  Dim temp As Decimal = 20.4D Dim s As String = String.Format("The temperature is {0}°C.", temp) Console.WriteLine(s) ' Displays 'The temperature is 20.4°C.'  格式 {0} 字串中的 是格式專案。 0 是物件的索引，其字串值將插入該位置。 （索引從0開始。如果要插入的物件不是字串，則會呼叫其 ToString 方法，將它轉換成其中一個，然後再將它插入結果字串中。 以下是另一個使用物件清單中的兩個格式專案和兩個物件的範例： string s = String.Format("At {0}, the temperature is {1}°C.", DateTime.Now, 20.4); Console.WriteLine(s); // Output similar to: 'At 4/10/2015 9:29:41 AM, the temperature is 20.4°C.'  String.Format("At {0}, the temperature is {1}°C.", DateTime.Now, 20.4) |> printfn "%s" // Output similar to: 'At 4/10/2015 9:29:41 AM, the temperature is 20.4°C.'  Dim s As String = String.Format("At {0}, the temperature is {1}°C.", Date.Now, 20.4) ' Output similar to: 'At 4/10/2015 9:29:41 AM, the temperature is 20.4°C.'  只要每個格式專案的索引在物件清單中具有相符的物件，就可以視需要在物件清單中擁有盡可能多的格式專案和多個物件。 您也不必擔心呼叫哪個多載;編譯程式會為您選取適當的編譯程式。 ### 控件格式設定 您可以使用格式字串追蹤格式專案中的索引，以控制物件的格式化方式。 例如， {0:d} 將 「d」 格式字串套用至物件清單中的第一個物件。 以下是單一物件和兩個格式專案的範例： string s = String.Format("It is now {0:d} at {0:t}", DateTime.Now); Console.WriteLine(s); // Output similar to: 'It is now 4/10/2015 at 10:04 AM'  String.Format("It is now {0:d} at {0:t}", DateTime.Now) |> printfn "%s" // Output similar to: 'It is now 4/10/2015 at 10:04 AM'  Dim s As String = String.Format("It is now {0:d} at {0:t}", Date.Now) ' Output similar to: 'It is now 4/10/2015 at 10:04 AM'  許多類型支援格式字串，包括所有數值類型（標準和自定義格式字元串）、所有日期和時間（標準和自定義格式字串）和時間間隔（標準和自定義格式字串）、所有列舉型別列舉型別和 GUID。 您也可以將格式字串的支援新增至您自己的類型。 ### 控制間距 您可以使用 之類的語法，定義插入至結果字串的字串寬度，其 {0,12}會插入 12 個字元的字串。 在此情況下，第一個物件的字串表示會在 12 個字元的欄位中靠右對齊。 （如果第一個物件的字串表示長度超過 12 個字元，則會忽略慣用的欄位寬度，並將整個字串插入結果字串中。 下列範例會定義 6 個字元的欄位來保存字串 「Year」 和某些年份字串，以及用來保存字串 「Population」 和某些母體數據的 15 個字元字段。 請注意，字元在欄位中靠右對齊。 int[] years = { 2013, 2014, 2015 }; int[] population = { 1025632, 1105967, 1148203 }; var sb = new System.Text.StringBuilder(); sb.Append(String.Format("{0,6} {1,15}\n\n", "Year", "Population")); for (int index = 0; index < years.Length; index++) sb.Append(String.Format("{0,6} {1,15:N0}\n", years[index], population[index])); Console.WriteLine(sb); // Result: // Year Population // // 2013 1,025,632 // 2014 1,105,967 // 2015 1,148,203  open System open System.Text let years = [| 2013; 2014; 2015 |] let population = [| 1025632; 1105967; 1148203 |] let sb = StringBuilder() sb.Append(String.Format("{0,6} {1,15}\n\n", "Year", "Population")) |> ignore for i = 0 to years.Length - 1 do sb.Append(String.Format("{0,6} {1,15:N0}\n", years[i], population[i])) |> ignore printfn$"{sb}"

// Result:
//      Year      Population
//
//      2013       1,025,632
//      2014       1,105,967
//      2015       1,148,203

Dim years() As Integer = {2013, 2014, 2015}
Dim population() As Integer = {1025632, 1105967, 1148203}
Dim sb As New StringBuilder()
sb.Append(String.Format("{0,6} {1,15}{2}{2}",
"Year", "Population", vbCrLf))
For index As Integer = 0 To years.Length - 1
sb.AppendFormat("{0,6} {1,15:N0}{2}",
years(index), population(index), vbCrLf)
Next
' Result:
'      Year      Population
'
'      2013       1,025,632
'      2014       1,105,967
'      2015       1,148,203


### 控件對齊方式

int[] years = { 2013, 2014, 2015 };
int[] population = { 1025632, 1105967, 1148203 };
String s = String.Format("{0,-10} {1,-10}\n\n", "Year", "Population");
for (int index = 0; index < years.Length; index++)
s += String.Format("{0,-10} {1,-10:N0}\n",
years[index], population[index]);
Console.WriteLine($"\n{s}"); // Result: // Year Population // // 2013 1,025,632 // 2014 1,105,967 // 2015 1,148,203  let years = [| 2013; 2014; 2015 |] let population = [| 1025632; 1105967; 1148203 |] let mutable s = String.Format("{0,-10} {1,-10}\n\n", "Year", "Population") for i = 0 to years.Length - 1 do s <- s + String.Format("{0,-10} {1,-10:N0}\n", years[i], population[i]) printfn$"\n{s}"
// Result:
//    Year       Population
//
//    2013       1,025,632
//    2014       1,105,967
//    2015       1,148,203

Dim years() As Integer = {2013, 2014, 2015}
Dim population() As Integer = {1025632, 1105967, 1148203}
Dim s As String = String.Format("{0,-10} {1,-10}{2}{2}",
"Year", "Population", vbCrLf)
For index As Integer = 0 To years.Length - 1
s += String.Format("{0,-10} {1,-10:N0}{2}",
years(index), population(index), vbCrLf)
Next
' Result:
'    Year       Population
'
'    2013       1,025,632
'    2014       1,105,967
'    2015       1,148,203


String.Format 會使用複合格式設定功能。 如需詳細資訊，請參閱複合格式設定

## 簡短的 Format 方法

DateTime dat = new DateTime(2012, 1, 17, 9, 30, 0);
string city = "Chicago";
int temp = -16;
string output = String.Format("At {0} in {1}, the temperature was {2} degrees.",
dat, city, temp);
Console.WriteLine(output);
// The example displays output like the following:
//    At 1/17/2012 9:30:00 AM in Chicago, the temperature was -16 degrees.

open System

let dat = DateTime(2012, 1, 17, 9, 30, 0)
let city = "Chicago"
let temp = -16
String.Format("At {0} in {1}, the temperature was {2} degrees.", dat, city, temp)
|> printfn "%s"
// The example displays output like the following:
//    At 1/17/2012 9:30:00 AM in Chicago, the temperature was -16 degrees.

Dim dat As Date = #1/17/2012 9:30AM#
Dim city As String = "Chicago"
Dim temp As Integer = -16
Dim output As String = String.Format("At {0} in {1}, the temperature was {2} degrees.",
dat, city, temp)
Console.WriteLine(output)
' The example displays the following output:
'    At 1/17/2012 9:30:00 AM in Chicago, the temperature was -16 degrees.


## 格式專案

{index[,alignment][:formatString]}


var value = String.Format("{0,-10:C}", 126347.89m);
Console.WriteLine(value);

open System

String.Format("{0,-10:C}", 126347.89m)
|> printfn "%s"

String.Format("{0,-10:C}", 126347.89D)


index

formatString

// Create array of 5-tuples with population data for three U.S. cities, 1940-1950.
Tuple<string, DateTime, int, DateTime, int>[] cities =
{ Tuple.Create("Los Angeles", new DateTime(1940, 1, 1), 1504277,
new DateTime(1950, 1, 1), 1970358),
Tuple.Create("New York", new DateTime(1940, 1, 1), 7454995,
new DateTime(1950, 1, 1), 7891957),
Tuple.Create("Chicago", new DateTime(1940, 1, 1), 3396808,
new DateTime(1950, 1, 1), 3620962),
Tuple.Create("Detroit", new DateTime(1940, 1, 1), 1623452,
new DateTime(1950, 1, 1), 1849568) };

"City", "Year", "Population", "Change (%)");
foreach (var city in cities) {
var output = String.Format("{0,-12}{1,8:yyyy}{2,12:N0}{3,8:yyyy}{4,12:N0}{5,14:P1}",
city.Item1, city.Item2, city.Item3, city.Item4, city.Item5,
(city.Item5 - city.Item3)/ (double)city.Item3);
Console.WriteLine(output);
}
// The example displays the following output:
//    City            Year  Population    Year  Population    Change (%)
//
//    Los Angeles     1940   1,504,277    1950   1,970,358        31.0 %
//    New York        1940   7,454,995    1950   7,891,957         5.9 %
//    Chicago         1940   3,396,808    1950   3,620,962         6.6 %
//    Detroit         1940   1,623,452    1950   1,849,568        13.9 %

// Create a list of 5-tuples with population data for three U.S. cities, 1940-1950.
let cities =
[ "Los Angeles", DateTime(1940, 1, 1), 1504277, DateTime(1950, 1, 1), 1970358
"New York", DateTime(1940, 1, 1), 7454995, DateTime(1950, 1, 1), 7891957
"Chicago", DateTime(1940, 1, 1), 3396808, DateTime(1950, 1, 1), 3620962
"Detroit", DateTime(1940, 1, 1), 1623452, DateTime(1950, 1, 1), 1849568 ]

String.Format("{0,-12}{1,8}{2,12}{1,8}{2,12}{3,14}\n", "City", "Year", "Population", "Change (%)")
|> printfn "%s"

for name, year1, pop1, year2, pop2 in cities do
String.Format("{0,-12}{1,8:yyyy}{2,12:N0}{3,8:yyyy}{4,12:N0}{5,14:P1}",
name, year1, pop1, year2, pop2,
double (pop2 - pop1) / double pop1)
|> printfn "%s"
// The example displays the following output:
//    City            Year  Population    Year  Population    Change (%)
//
//    Los Angeles     1940   1,504,277    1950   1,970,358        31.0 %
//    New York        1940   7,454,995    1950   7,891,957         5.9 %
//    Chicago         1940   3,396,808    1950   3,620,962         6.6 %
//    Detroit         1940   1,623,452    1950   1,849,568        13.9 %

Module Example3
Public Sub Main()
' Create array of 5-tuples with population data for three U.S. cities, 1940-1950.
Dim cities() =
{Tuple.Create("Los Angeles", #1/1/1940#, 1504277, #1/1/1950#, 1970358),
Tuple.Create("New York", #1/1/1940#, 7454995, #1/1/1950#, 7891957),
Tuple.Create("Chicago", #1/1/1940#, 3396808, #1/1/1950#, 3620962),
Tuple.Create("Detroit", #1/1/1940#, 1623452, #1/1/1950#, 1849568)}

Dim header As String = String.Format("{0,-12}{1,8}{2,12}{1,8}{2,12}{3,14}",
"City", "Year", "Population", "Change (%)")
Console.WriteLine()
For Each city In cities
Dim output = String.Format("{0,-12}{1,8:yyyy}{2,12:N0}{3,8:yyyy}{4,12:N0}{5,14:P1}",
city.Item1, city.Item2, city.Item3, city.Item4, city.Item5,
(city.Item5 - city.Item3) / city.Item3)
Console.WriteLine(output)
Next
End Sub
End Module
' The example displays the following output:
'    City            Year  Population    Year  Population    Change (%)
'
'    Los Angeles     1940   1,504,277    1950   1,970,358        31.0 %
'    New York        1940   7,454,995    1950   7,891,957         5.9 %
'    Chicago         1940   3,396,808    1950   3,620,962         6.6 %
'    Detroit         1940   1,623,452    1950   1,849,568        13.9 %


## 格式化具有相同索引的專案

short[] values= { Int16.MinValue, -27, 0, 1042, Int16.MaxValue };
Console.WriteLine("{0,10}  {1,10}\n", "Decimal", "Hex");
foreach (short value in values)
{
string formatString = String.Format("{0,10:G}: {0,10:X}", value);
Console.WriteLine(formatString);
}
// The example displays the following output:
//       Decimal         Hex
//
//        -32768:       8000
//           -27:       FFE5
//             0:          0
//          1042:        412
//         32767:       7FFF

open System

let values= [| Int16.MinValue; -27s; 0s; 1042s; Int16.MaxValue |]
printfn "%10s  %10s\n" "Decimal" "Hex"
for value in values do
String.Format("{0,10:G}: {0,10:X}", value)
|> printfn "%s"
// The example displays the following output:
//       Decimal         Hex
//
//        -32768:       8000
//           -27:       FFE5
//             0:          0
//          1042:        412
//         32767:       7FFF

Module Example1
Public Sub Main()
Dim values() As Short = {Int16.MinValue, -27, 0, 1042, Int16.MaxValue}
Console.WriteLine("{0,10}  {1,10}", "Decimal", "Hex")
Console.WriteLine()
For Each value As Short In values
Dim formatString As String = String.Format("{0,10:G}: {0,10:X}", value)
Console.WriteLine(formatString)
Next
End Sub
End Module
' The example displays the following output:
'       Decimal         Hex
'
'        -32768:       8000
'           -27:       FFE5
'             0:          0
'          1042:        412
'         32767:       7FFF


## 自訂格式設定作業

### 範例：自定義格式設定作業

using System;

public class TestFormatter
{
public static void Main()
{
int acctNumber = 79203159;
Console.WriteLine(String.Format(new CustomerFormatter(), "{0}", acctNumber));
Console.WriteLine(String.Format(new CustomerFormatter(), "{0:G}", acctNumber));
Console.WriteLine(String.Format(new CustomerFormatter(), "{0:S}", acctNumber));
Console.WriteLine(String.Format(new CustomerFormatter(), "{0:P}", acctNumber));
try {
Console.WriteLine(String.Format(new CustomerFormatter(), "{0:X}", acctNumber));
}
catch (FormatException e) {
Console.WriteLine(e.Message);
}
}
}

public class CustomerFormatter : IFormatProvider, ICustomFormatter
{
public object GetFormat(Type formatType)
{
if (formatType == typeof(ICustomFormatter))
return this;
else
return null;
}

public string Format(string format,
object arg,
IFormatProvider formatProvider)
{
if (! this.Equals(formatProvider))
{
return null;
}
else
{
if (String.IsNullOrEmpty(format))
format = "G";

string customerString = arg.ToString();
if (customerString.Length < 8)

format = format.ToUpper();
switch (format)
{
case "G":
return customerString.Substring(0, 1) + "-" +
customerString.Substring(1, 5) + "-" +
customerString.Substring(6);
case "S":
return customerString.Substring(0, 1) + "/" +
customerString.Substring(1, 5) + "/" +
customerString.Substring(6);
case "P":
return customerString.Substring(0, 1) + "." +
customerString.Substring(1, 5) + "." +
customerString.Substring(6);
default:
throw new FormatException(
String.Format("The '{0}' format specifier is not supported.", format));
}
}
}
}
// The example displays the following output:
//       7-92031-59
//       7-92031-59
//       7/92031/59
//       7.92031.59
//       The 'X' format specifier is not supported.

open System

type CustomerFormatter() =
interface IFormatProvider with
member this.GetFormat(formatType) =
if formatType = typeof<ICustomFormatter> then
this
else
null

interface ICustomFormatter with
member this.Format(format, arg, formatProvider: IFormatProvider) =
if this.Equals formatProvider |> not then
null
else
let format =
if String.IsNullOrEmpty format then "G"
else format.ToUpper()

let customerString =
let s = string arg
if s.Length < 8 then
else s

match format with
| "G" ->
customerString.Substring(0, 1) + "-" +
customerString.Substring(1, 5) + "-" +
customerString.Substring 6
| "S" ->
customerString.Substring(0, 1) + "/" +
customerString.Substring(1, 5) + "/" +
customerString.Substring 6
| "P" ->
customerString.Substring(0, 1) + "." +
customerString.Substring(1, 5) + "." +
customerString.Substring 6
| _ ->
raise (FormatException $"The '{format}' format specifier is not supported.") let acctNumber = 79203159 String.Format(CustomerFormatter(), "{0}", acctNumber) |> printfn "%s" String.Format(CustomerFormatter(), "{0:G}", acctNumber) |> printfn "%s" String.Format(CustomerFormatter(), "{0:S}", acctNumber) |> printfn "%s" String.Format(CustomerFormatter(), "{0:P}", acctNumber) |> printfn "%s" try String.Format(CustomerFormatter(), "{0:X}", acctNumber) |> printfn "%s" with :? FormatException as e -> printfn$"{e.Message}"

// The example displays the following output:
//       7-92031-59
//       7-92031-59
//       7/92031/59
//       7.92031.59
//       The 'X' format specifier is not supported.

Module TestFormatter
Public Sub Main()
Dim acctNumber As Integer = 79203159
Console.WriteLine(String.Format(New CustomerFormatter, "{0}", acctNumber))
Console.WriteLine(String.Format(New CustomerFormatter, "{0:G}", acctNumber))
Console.WriteLine(String.Format(New CustomerFormatter, "{0:S}", acctNumber))
Console.WriteLine(String.Format(New CustomerFormatter, "{0:P}", acctNumber))
Try
Console.WriteLine(String.Format(New CustomerFormatter, "{0:X}", acctNumber))
Catch e As FormatException
Console.WriteLine(e.Message)
End Try
End Sub
End Module

Public Class CustomerFormatter : Implements IFormatProvider, ICustomFormatter
Public Function GetFormat(type As Type) As Object  _
Implements IFormatProvider.GetFormat
If type Is GetType(ICustomFormatter) Then
Return Me
Else
Return Nothing
End If
End Function

Public Function Format(fmt As String, _
arg As Object, _
formatProvider As IFormatProvider) As String _
Implements ICustomFormatter.Format
If Not Me.Equals(formatProvider) Then
Return Nothing
Else
If String.IsNullOrEmpty(fmt) Then fmt = "G"

Dim customerString As String = arg.ToString()
if customerString.Length < 8 Then _

Select Case fmt
Case "G"
Return customerString.Substring(0, 1) & "-" & _
customerString.Substring(1, 5) & "-" & _
customerString.Substring(6)
Case "S"
Return customerString.Substring(0, 1) & "/" & _
customerString.Substring(1, 5) & "/" & _
customerString.Substring(6)
Case "P"
Return customerString.Substring(0, 1) & "." & _
customerString.Substring(1, 5) & "." & _
customerString.Substring(6)
Case Else
Throw New FormatException( _
String.Format("The '{0}' format specifier is not supported.", fmt))
End Select
End If
End Function
End Class
' The example displays the following output:
'       7-92031-59
'       7-92031-59
'       7/92031/59
'       7.92031.59
'       The 'X' format specifier is not supported.


### 範例：攔截提供者和羅馬數位格式器

• 它會顯示傳遞至其 ICustomFormatter.Format 實作的參數。 這可讓我們查看方法針對嘗試格式化的每個對象傳遞至自定義格式實作的參數 Format(IFormatProvider, String, Object[]) 。 當您偵錯應用程式時，這非常有用。

• 如果要格式化的物件是使用 「R」 標準格式字串格式化的不帶正負號位元組值，則自訂格式器會將數值格式化為羅馬數位。

using System;
using System.Globalization;

public class InterceptProvider : IFormatProvider, ICustomFormatter
{
public object GetFormat(Type formatType)
{
if (formatType == typeof(ICustomFormatter))
return this;
else
return null;
}

public string Format(String format, Object obj, IFormatProvider provider)
{
// Display information about method call.
string formatString = format ?? "<null>";
Console.WriteLine("Provider: {0}, Object: {1}, Format String: {2}",
provider.GetType().Name, obj ?? "<null>", formatString);

if (obj == null) return String.Empty;

// If this is a byte and the "R" format string, format it with Roman numerals.
if (obj is Byte && formatString.ToUpper().Equals("R")) {
Byte value = (Byte) obj;
int remainder;
int result;
String returnString = String.Empty;

// Get the hundreds digit(s)
result = Math.DivRem(value, 100, out remainder);
if (result > 0)
returnString = new String('C', result);
value = (Byte) remainder;
// Get the 50s digit
result = Math.DivRem(value, 50, out remainder);
if (result == 1)
returnString += "L";
value = (Byte) remainder;
// Get the tens digit.
result = Math.DivRem(value, 10, out remainder);
if (result > 0)
returnString += new String('X', result);
value = (Byte) remainder;
// Get the fives digit.
result = Math.DivRem(value, 5, out remainder);
if (result > 0)
returnString += "V";
value = (Byte) remainder;
if (remainder > 0)
returnString += new String('I', remainder);

// Check whether we have too many X characters.
int pos = returnString.IndexOf("XXXX");
if (pos >= 0) {
int xPos = returnString.IndexOf("L");
if (xPos >= 0 & xPos == pos - 1)
returnString = returnString.Replace("LXXXX", "XC");
else
returnString = returnString.Replace("XXXX", "XL");
}
// Check whether we have too many I characters
pos = returnString.IndexOf("IIII");
if (pos >= 0)
if (returnString.IndexOf("V") >= 0)
returnString = returnString.Replace("VIIII", "IX");
else
returnString = returnString.Replace("IIII", "IV");

return returnString;
}

// Use default for all other formatting.
if (obj is IFormattable)
return ((IFormattable) obj).ToString(format, CultureInfo.CurrentCulture);
else
return obj.ToString();
}
}

public class Example
{
public static void Main()
{
int n = 10;
double value = 16.935;
DateTime day = DateTime.Now;
InterceptProvider provider = new InterceptProvider();
Console.WriteLine(String.Format(provider, "{0:N0}: {1:C2} on {2:d}\n", n, value, day));
Console.WriteLine(String.Format(provider, "{0}: {1:F}\n", "Today: ",
(DayOfWeek) DateTime.Now.DayOfWeek));
Console.WriteLine(String.Format(provider, "{0:X}, {1}, {2}\n",
(Byte) 2, (Byte) 12, (Byte) 199));
Console.WriteLine(String.Format(provider, "{0:R}, {1:R}, {2:R}\n",
(Byte) 2, (Byte) 12, (Byte) 199));
}
}
// The example displays the following output:
//    Provider: InterceptProvider, Object: 10, Format String: N0
//    Provider: InterceptProvider, Object: 16.935, Format String: C2
//    Provider: InterceptProvider, Object: 1/31/2013 6:10:28 PM, Format String: d
//    10: $16.94 on 1/31/2013 // // Provider: InterceptProvider, Object: Today: , Format String: <null> // Provider: InterceptProvider, Object: Thursday, Format String: F // Today: : Thursday // // Provider: InterceptProvider, Object: 2, Format String: X // Provider: InterceptProvider, Object: 12, Format String: <null> // Provider: InterceptProvider, Object: 199, Format String: <null> // 2, 12, 199 // // Provider: InterceptProvider, Object: 2, Format String: R // Provider: InterceptProvider, Object: 12, Format String: R // Provider: InterceptProvider, Object: 199, Format String: R // II, XII, CXCIX  open System open System.Globalization type InterceptProvider() = interface IFormatProvider with member this.GetFormat(formatType) = if formatType = typeof<ICustomFormatter> then this else null interface ICustomFormatter with member _.Format(format, obj, provider: IFormatProvider) = // Display information about method call. let formatString = if format = null then "<null>" else format printfn$"Provider: {provider.GetType().Name}, Object: %A{obj}, Format String: %s{formatString}"

if obj = null then
String.Empty
else
// If this is a byte and the "R" format string, format it with Roman numerals.
match obj with
| :? byte as value when formatString.ToUpper().Equals "R" ->
let mutable returnString = String.Empty

// Get the hundreds digit(s)
let struct (result, remainder) = Math.DivRem(value, 100uy)
if result > 0uy then
returnString <- String('C', int result)
let value = byte remainder
// Get the 50s digit
let struct (result, remainder) = Math.DivRem(value, 50uy)
if result = 1uy then
returnString <- returnString + "L"
let value = byte remainder
// Get the tens digit.
let struct (result, remainder) = Math.DivRem(value, 10uy)
if result > 0uy then
returnString <- returnString + String('X', int result)
let value = byte remainder
// Get the fives digit.
let struct (result, remainder) = Math.DivRem(value, 5uy)
if result > 0uy then
returnString <- returnString + "V"
let value = byte remainder
if remainder > 0uy then
returnString <- returnString + String('I', int remainder)

// Check whether we have too many X characters.
let pos = returnString.IndexOf "XXXX"
if pos >= 0 then
let xPos = returnString.IndexOf "L"
returnString <-
if xPos >= 0 && xPos = pos - 1 then
returnString.Replace("LXXXX", "XC")
else
returnString.Replace("XXXX", "XL")
// Check whether we have too many I characters
let pos = returnString.IndexOf "IIII"
if pos >= 0 then
returnString <-
if returnString.IndexOf "V" >= 0 then
returnString.Replace("VIIII", "IX")
else
returnString.Replace("IIII", "IV")
returnString

// Use default for all other formatting.
| :? IFormattable as x ->
x.ToString(format, CultureInfo.CurrentCulture)
| _ ->
string obj

let n = 10
let value = 16.935
let day = DateTime.Now
let provider = InterceptProvider()
String.Format(provider, "{0:N0}: {1:C2} on {2:d}\n", n, value, day)
|> printfn "%s"
String.Format(provider, "{0}: {1:F}\n", "Today: ", DateTime.Now.DayOfWeek)
|> printfn "%s"
String.Format(provider, "{0:X}, {1}, {2}\n", 2uy, 12uy, 199uy)
|> printfn "%s"
String.Format(provider, "{0:R}, {1:R}, {2:R}\n", 2uy, 12uy, 199uy)
|> printfn "%s"
// The example displays the following output:
//    Provider: InterceptProvider, Object: 10, Format String: N0
//    Provider: InterceptProvider, Object: 16.935, Format String: C2
//    Provider: InterceptProvider, Object: 1/31/2013 6:10:28 PM, Format String: d
//    10: $16.94 on 1/31/2013 // // Provider: InterceptProvider, Object: Today: , Format String: <null> // Provider: InterceptProvider, Object: Thursday, Format String: F // Today: : Thursday // // Provider: InterceptProvider, Object: 2, Format String: X // Provider: InterceptProvider, Object: 12, Format String: <null> // Provider: InterceptProvider, Object: 199, Format String: <null> // 2, 12, 199 // // Provider: InterceptProvider, Object: 2, Format String: R // Provider: InterceptProvider, Object: 12, Format String: R // Provider: InterceptProvider, Object: 199, Format String: R // II, XII, CXCIX  Imports System.Globalization Public Class InterceptProvider : Implements IFormatProvider, ICustomFormatter Public Function GetFormat(formatType As Type) As Object _ Implements IFormatProvider.GetFormat If formatType Is GetType(ICustomFormatter) Then Return Me Else Return Nothing End If End Function Public Function Format(fmt As String, obj As Object, provider As IFormatProvider) As String _ Implements ICustomFormatter.Format Dim formatString As String = If(fmt IsNot Nothing, fmt, "<null>") Console.WriteLine("Provider: {0}, Object: {1}, Format String: {2}", provider, If(obj IsNot Nothing, obj, "<null>"), formatString) If obj Is Nothing Then Return String.Empty ' If this is a byte and the "R" format string, format it with Roman numerals. If TypeOf(obj) Is Byte AndAlso formatString.ToUpper.Equals("R") Then Dim value As Byte = CByte(obj) Dim remainder As Integer Dim result As Integer Dim returnString As String = String.Empty ' Get the hundreds digit(s) result = Math.DivRem(value, 100, remainder) If result > 0 Then returnString = New String("C"c, result) value = CByte(remainder) ' Get the 50s digit result = Math.DivRem(value, 50, remainder) If result = 1 Then returnString += "L" value = CByte(remainder) ' Get the tens digit. result = Math.DivRem(value, 10, remainder) If result > 0 Then returnString += New String("X"c, result) value = CByte(remainder) ' Get the fives digit. result = Math.DivRem(value, 5, remainder) If result > 0 Then returnString += "V" value = CByte(remainder) ' Add the ones digit. If remainder > 0 Then returnString += New String("I"c, remainder) ' Check whether we have too many X characters. Dim pos As Integer = returnString.IndexOf("XXXX") If pos >= 0 Then Dim xPos As Integer = returnString.IndexOf("L") If xPos >= 0 And xPos = pos - 1 Then returnString = returnString.Replace("LXXXX", "XC") Else returnString = returnString.Replace("XXXX", "XL") End If End If ' Check whether we have too many I characters pos = returnString.IndexOf("IIII") If pos >= 0 Then If returnString.IndexOf("V") >= 0 Then returnString = returnString.Replace("VIIII", "IX") Else returnString = returnString.Replace("IIII", "IV") End If End If Return returnString End If ' Use default for all other formatting. If obj Is GetType(IFormattable) Return CType(obj, IFormattable).ToString(fmt, CultureInfo.CurrentCulture) Else Return obj.ToString() End If End Function End Class Module Example Public Sub Main() Dim n As Integer = 10 Dim value As Double = 16.935 Dim day As DateTime = Date.Now Dim provider As New InterceptProvider() Console.WriteLine(String.Format(provider, "{0:N0}: {1:C2} on {2:d}", n, value, day)) Console.WriteLine() Console.WriteLine(String.Format(provider, "{0}: {1:F}", "Today", CType(Date.Now.DayOfWeek, DayOfWeek))) Console.WriteLine() Console.WriteLine(String.Format(provider, "{0:X}, {1}, {2}\n", CByte(2), CByte(12), CByte(199))) Console.WriteLine() Console.WriteLine(String.Format(provider, "{0:R}, {1:R}, {2:R}", CByte(2), CByte(12), CByte(199))) End Sub End Module ' The example displays the following output: ' Provider: InterceptProvider, Object: 10, Format String: N0 ' Provider: InterceptProvider, Object: 16.935, Format String: C2 ' Provider: InterceptProvider, Object: 1/31/2013 6:10:28 PM, Format String: d ' 10:$16.94 on 1/31/2013
'
'    Provider: InterceptProvider, Object: Today: , Format String: <null>
'    Provider: InterceptProvider, Object: Thursday, Format String: F
'    Today: : Thursday
'
'    Provider: InterceptProvider, Object: 2, Format String: X
'    Provider: InterceptProvider, Object: 12, Format String: <null>
'    Provider: InterceptProvider, Object: 199, Format String: <null>
'    2, 12, 199
'
'    Provider: InterceptProvider, Object: 2, Format String: R
'    Provider: InterceptProvider, Object: 12, Format String: R
'    Provider: InterceptProvider, Object: 199, Format String: R
'    II, XII, CXCIX


## 常見問題集

### 為何建議對 方法的 String.Format 呼叫進行字串插補？

• 更有彈性。 它可以在任何字串中使用，而不需要呼叫支持複合格式的方法。 否則，您必須呼叫 Format 方法或其他支援複合格式的方法，例如 Console.WriteLineStringBuilder.AppendFormat

• 更容易閱讀。 因為要插入字串的表達式會出現在插入運算式中，而不是出現在自變數清單中，插入字串比程式代碼和讀取更容易。 由於字串的可讀性更高，插補字串不僅可以取代對複合格式方法的呼叫，還可以用於字串串連作業，以產生更簡潔、更清楚的程序代碼。

string[] names = { "Balto", "Vanya", "Dakota", "Samuel", "Koani", "Yiska", "Yuma" };
string output = names[0] + ", " + names[1] + ", " + names[2] + ", " +
names[3] + ", " + names[4] + ", " + names[5] + ", " +
names[6];

output += "\n";
var date = DateTime.Now;
output += String.Format("It is {0:t} on {0:d}. The day of the week is {1}.",
date, date.DayOfWeek);
Console.WriteLine(output);
// The example displays the following output:
//     Balto, Vanya, Dakota, Samuel, Koani, Yiska, Yuma
//     It is 10:29 AM on 1/8/2018. The day of the week is Monday.

open System

let names = [| "Balto"; "Vanya"; "Dakota"; "Samuel"; "Koani"; "Yiska"; "Yuma" |]
let output =
names[0] + ", " + names[1] + ", " + names[2] + ", " +
names[3] + ", " + names[4] + ", " + names[5] + ", " +
names[6] + "\n"

let date = DateTime.Now
output + String.Format("It is {0:t} on {0:d}. The day of the week is {1}.", date, date.DayOfWeek)
|> printfn "%s"
// The example displays the following output:
//     Balto, Vanya, Dakota, Samuel, Koani, Yiska, Yuma
//     It is 10:29 AM on 1/8/2018. The day of the week is Monday.


Module Example12
Public Sub Main()
Dim names = {"Balto", "Vanya", "Dakota", "Samuel", "Koani", "Yiska", "Yuma"}
Dim output = names(0) + ", " + names(1) + ", " + names(2) + ", " +
names(3) + ", " + names(4) + ", " + names(5) + ", " +
names(6)

output += vbCrLf
Dim dat = DateTime.Now
output += String.Format("It is {0:t} on {0:d}. The day of the week is {1}.",
dat, dat.DayOfWeek)
Console.WriteLine(output)
End Sub
End Module
' The example displays the following output:
'     Balto, Vanya, Dakota, Samuel, Koani, Yiska, Yuma
'     It is 10:29 AM on 1/8/2018. The day of the week is Monday.


string[] names = { "Balto", "Vanya", "Dakota", "Samuel", "Koani", "Yiska", "Yuma" };
string output = $"{names[0]}, {names[1]}, {names[2]}, {names[3]}, {names[4]}, " +$"{names[5]}, {names[6]}";

var date = DateTime.Now;
output += $"\nIt is {date:t} on {date:d}. The day of the week is {date.DayOfWeek}."; Console.WriteLine(output); // The example displays the following output: // Balto, Vanya, Dakota, Samuel, Koani, Yiska, Yuma // It is 10:29 AM on 1/8/2018. The day of the week is Monday.  open System let names = [| "Balto"; "Vanya"; "Dakota"; "Samuel"; "Koani"; "Yiska"; "Yuma" |] let output =$"{names[0]}, {names[1]}, {names[2]}, {names[3]}, {names[4]}, {names[5]}, {names[6]}"

let date = DateTime.Now
output + $"\nIt is {date:t} on {date:d}. The day of the week is {date.DayOfWeek}." |> printfn "%s" // The example displays the following output: // Balto, Vanya, Dakota, Samuel, Koani, Yiska, Yuma // It is 10:29 AM on 1/8/2018. The day of the week is Monday.   Module Example13 Public Sub Main() Dim names = {"Balto", "Vanya", "Dakota", "Samuel", "Koani", "Yiska", "Yuma"} Dim output =$"{names(0)}, {names(1)}, {names(2)}, {names(3)}, {names(4)}, " +
$"{names(5)}, {names(6)}" Dim dat = DateTime.Now output +=$"{vbCrLf}It is {dat:t} on {dat:d}. The day of the week is {dat.DayOfWeek}."
Console.WriteLine(output)
End Sub
End Module
' The example displays the following output:
'     Balto, Vanya, Dakota, Samuel, Koani, Yiska, Yuma
'     It is 10:29 AM on 1/8/2018. The day of the week is Monday.


### 如何? 控制取代格式項目的結果字串對齊方式？

{index[,alignment][: formatString]}


### 如何? 控制小數分隔符後面的位數？

object[] values = { 1603, 1794.68235, 15436.14 };
string result;
foreach (var value in values)
{
result = String.Format("{0,12:C2}   {0,12:E3}   {0,12:F4}   {0,12:N3}  {1,12:P2}\n",
Convert.ToDouble(value), Convert.ToDouble(value) / 10000);
Console.WriteLine(result);
}
// The example displays output like the following:
//       $1,603.00 1.603E+003 1603.0000 1,603.000 16.03 % // //$1,794.68     1.795E+003      1794.6824      1,794.682       17.95 %
//
//      $15,436.14 1.544E+004 15436.1400 15,436.140 154.36 %  open System let values: obj list = [ 1603, 1794.68235, 15436.14 ] for value in values do String.Format("{0,12:C2} {0,12:E3} {0,12:F4} {0,12:N3} {1,12:P2}\n", Convert.ToDouble(value), Convert.ToDouble(value) / 10000.) |> printfn "%s" // The example displays output like the following: //$1,603.00     1.603E+003      1603.0000      1,603.000       16.03 %
//
//       $1,794.68 1.795E+003 1794.6824 1,794.682 17.95 % // //$15,436.14     1.544E+004     15436.1400     15,436.140      154.36 %

Module Example7
Public Sub Main()
Dim values() As Object = {1603, 1794.68235, 15436.14}
Dim result As String
For Each value In values
result = String.Format("{0,12:C2}   {0,12:E3}   {0,12:F4}   {0,12:N3}  {1,12:P2}",
value, CDbl(value) / 10000)
Console.WriteLine(result)
Console.WriteLine()
Next
End Sub
End Module
' The example displays the following output:
'       $1,603.00 1.603E+003 1603.0000 1,603.000 16.03 % ' '$1,794.68     1.795E+003      1794.6824      1,794.682       17.95 %
'
'      \$15,436.14     1.544E+004     15436.1400     15,436.140      154.36 %


decimal value = 16309.5436m;
string result = String.Format("{0,12:#.00000} {0,12:0,000.00} {0,12:000.00#}",
value);
Console.WriteLine(result);
// The example displays the following output:
//        16309.54360    16,309.54    16309.544

let value = 16309.5436m
String.Format("{0,12:#.00000} {0,12:0,000.00} {0,12:000.00#}", value)
|> printfn "%s"
// The example displays the following output:
//        16309.54360    16,309.54    16309.544

Module Example8
Public Sub Main()
Dim value As Decimal = 16309.5436D
Dim result As String = String.Format("{0,12:#.00000} {0,12:0,000.00} {0,12:000.00#}",
value)
Console.WriteLine(result)
End Sub
End Module
' The example displays the following output:
'    16309.54360    16,309.54    16309.544


### 如何? 控制整數位數嗎？

int value = 1326;
string result = String.Format("{0,10:D6} {0,10:X8}", value);
Console.WriteLine(result);
// The example displays the following output:
//     001326   0000052E

open System

let value = 1326
String.Format("{0,10:D6} {0,10:X8}", value)
|> printfn "%s"
// The example displays the following output:
//     001326   0000052E

Module Example10
Public Sub Main()
Dim value As Integer = 1326
Dim result As String = String.Format("{0,10:D6} {0,10:X8}", value)
Console.WriteLine(result)
End Sub
End Module
' The example displays the following output:
'       001326   0000052E


int value = 16342;
string result = String.Format("{0,18:00000000} {0,18:00000000.000} {0,18:000,0000,000.0}",
value);
Console.WriteLine(result);
// The example displays the following output:
//           00016342       00016342.000    0,000,016,342.0

open System

let value = 16342
String.Format("{0,18:00000000} {0,18:00000000.000} {0,18:000,0000,000.0}", value)
|> printfn "%s"
// The example displays the following output:
//           00016342       00016342.000    0,000,016,342.0

Module Example9
Public Sub Main()
Dim value As Integer = 16342
Dim result As String = String.Format("{0,18:00000000} {0,18:00000000.000} {0,18:000,0000,000.0}",
value)
Console.WriteLine(result)
End Sub
End Module
' The example displays the following output:
'           00016342       00016342.000    0,000,016,342.0


### 如何? 結果字串中包含常值大括弧 （“{” 和 “}”？

result = String.Format("The text has {0} '{' characters and {1} '}' characters.",
nOpen, nClose);

let result =
String.Format("The text has {0} '{' characters and {1} '}' characters.", nOpen, nClose)

result = String.Format("The text has {0} '{' characters and {1} '}' characters.",
nOpen, nClose)


string result;
int nOpen = 1;
int nClose = 2;
result = String.Format("The text has {0} '{{' characters and {1} '}}' characters.",
nOpen, nClose);
Console.WriteLine(result);

let result =
String.Format("The text has {0} '{{' characters and {1} '}}' characters.", nOpen, nClose)

result = String.Format("The text has {0} '{{' characters and {1} '}}' characters.",
nOpen, nClose)


string result;
int nOpen = 1;
int nClose = 2;
result = String.Format("The text has {0} '{1}' characters and {2} '{3}' characters.",
nOpen, "{", nClose, "}");
Console.WriteLine(result);

let result =
String.Format("The text has {0} '{1}' characters and {2} '{3}' characters.", nOpen, "{", nClose, "}")

result = String.Format("The text has {0} '{1}' characters and {2} '{3}' characters.",
nOpen, "{", nClose, "}")


### 如果 Format（System.IFormatProvider，System.String，System.Object[]） 方法支持參數數位，為什麼我的程式代碼在使用數位時擲回例外狀況？

Random rnd = new Random();
int[] numbers = new int[4];
int total = 0;
for (int ctr = 0; ctr <= 2; ctr++)
{
int number = rnd.Next(1001);
numbers[ctr] = number;
total += number;
}
numbers[3] = total;
Console.WriteLine("{0} + {1} + {2} = {3}", numbers);

open System

let rnd = Random()
let mutable total = 0
let numbers = Array.zeroCreate<int> 4
for i = 0 to 2 do
let number = rnd.Next 1001
numbers[i] <- number
total <- total + number
numbers[3] <- total
Console.WriteLine("{0} + {1} + {2} = {3}", numbers)

Imports System.Collections.Generic

Module Example5
Public Sub Main()
Dim rnd As New Random()
Dim numbers(3) As Integer
Dim total As Integer = 0
For ctr = 0 To 2
Dim number As Integer = rnd.Next(1001)
numbers(ctr) = number
total += number
Next
numbers(3) = total
Console.WriteLine("{0} + {1} + {2} = {3}", numbers)
End Sub
End Module


Random rnd = new Random();
int[] numbers = new int[4];
int total = 0;
for (int ctr = 0; ctr <= 2; ctr++)
{
int number = rnd.Next(1001);
numbers[ctr] = number;
total += number;
}
numbers[3] = total;
object[] values = new object[numbers.Length];
numbers.CopyTo(values, 0);
Console.WriteLine("{0} + {1} + {2} = {3}", values);

open System

let rnd = Random()
let numbers = Array.zeroCreate<int> 4
let mutable total = 0
for i = 0 to 2 do
let number = rnd.Next 1001
numbers[i] <- number
total <- total + number
numbers[3] <- total
let values = Array.zeroCreate<obj> numbers.Length
numbers.CopyTo(values, 0)
Console.WriteLine("{0} + {1} + {2} = {3}", values)

Imports System.Collections.Generic

Module Example6
Public Sub Main()
Dim rnd As New Random()
Dim numbers(3) As Integer
Dim total As Integer = 0
For ctr = 0 To 2
Dim number As Integer = rnd.Next(1001)
numbers(ctr) = number
total += number
Next
numbers(3) = total
Dim values(numbers.Length - 1) As Object
numbers.CopyTo(values, 0)
Console.WriteLine("{0} + {1} + {2} = {3}", values)
End Sub
End Module