F# 支援使用 printf、 printfn、 sprintf和相關函式來檢查純文字的類型檢查格式。
例如,
dotnet fsi
> printfn "Hello %s, %d + %d is %d" "world" 2 2 (2+2);;
提供輸出
Hello world, 2 + 2 is 4
F# 也允許結構化值格式化為純文字。 例如,請考慮下列範例,將輸出格式化為類似矩陣的 Tuple 顯示。
dotnet fsi
> printfn "%A" [ for i in 1 .. 5 -> [ for j in 1 .. 5 -> (i, j) ] ];;
[[(1, 1); (1, 2); (1, 3); (1, 4); (1, 5)];
[(2, 1); (2, 2); (2, 3); (2, 4); (2, 5)];
[(3, 1); (3, 2); (3, 3); (3, 4); (3, 5)];
[(4, 1); (4, 2); (4, 3); (4, 4); (4, 5)];
[(5, 1); (5, 2); (5, 3); (5, 4); (5, 5)]]
當您在格式化字串中使用%Aprintf格式時,會啟動結構化純文字格式設定。
當格式化 F# 互動式中的值輸出時,也會啟動它,其中輸出包含額外的資訊,而且可另外自定義。
純文本格式設定也可以透過 F# 等位和記錄值的任何呼叫 x.ToString() 來觀察,包括偵錯、記錄和其他工具中隱含發生的呼叫。
檢查 printf格式字串
如果 printf 格式函式與格式字串中的 printf 格式規範不符的自變數搭配使用,將會報告編譯時期錯誤。 例如,
sprintf "Hello %s" (2+2)
提供輸出
sprintf "Hello %s" (2+2)
----------------------^
stdin(3,25): error FS0001: The type 'string' does not match the type 'int'
從技術上講,使用 printf 和其他相關函式時,F# 編譯程式中的特殊規則會檢查傳遞為格式字串的字串常值,確保套用的後續自變數是正確的類型,以符合所使用的格式規範。
的格式規範 printf
格式的格式規格 printf 是具有 % 指出格式之標記的字串。 格式佔位元是由類型解譯的位置所組成 %[flags][width][.precision][type] ,如下所示:
| 格式規範 | Type(s) | 備註 |
|---|---|---|
%b |
bool (System.Boolean) |
格式化為 true 或 false |
%s |
string (System.String) |
格式化為其未逸出的內容 |
%c |
char (System.Char) |
格式化為字元常值 |
%d、%i |
基本整數類型 | 格式化為十進位整數,如果基本整數類型帶正負號,則為帶正負號 |
%u |
基本整數類型 | 格式化為不帶正負號的十進位整數 |
%x、%X |
基本整數類型 | 格式化為不帶正負號的十六進位數位(分別十六進位數位的 a-f 或 A-F) |
%o |
基本整數類型 | 格式化為不帶正負號的八進位數位 |
%B |
基本整數類型 | 格式化為無符號二進位數 |
%e、%E |
基本浮點類型 | 格式化為帶正負號的值, [-]d.dddde[sign]ddd 其中 d 為單一十進位數,dddd 是一或多個十進位數,ddd 正好是三個十進制數,而符號為 + 或 - |
%f、%F |
基本浮點類型 | 格式化為具有 格式 [-]dddd.dddd的帶正負號值,其中 dddd 是一或多個十進制數。 小數點之前的位數取決於數位的大小,小數點後面的位數取決於所要求的有效位數。 |
%g、%G |
基本浮點類型 | 使用 格式化為列印 %f 或 %e 格式的帶正負號值,無論哪個值和精確度都比較精簡。 |
%M |
a decimal (System.Decimal) 值 |
使用 "G" 的格式規範格式化 System.Decimal.ToString(format) |
%O |
任何值 | 格式化物件並呼叫其 System.Object.ToString() 方法 |
%A |
任何值 | 使用 結構化純文字格式 設定與預設版面配置設定格式化 |
%a |
任何值 | 需要兩個自變數:接受內容參數和值的格式化函式,以及要列印的特定值 |
%t |
任何值 | 需要一個自變數:接受可輸出或傳回適當文字之內容參數的格式函式 |
%% |
(無) | 不需要任何自變數,並列印純百分比符號: % |
基本整數類型為byte(System.Byte)、()、 int16sbyteSystem.Int16(System.SByte)、 uint16System.UInt32uint32System.Int64System.Int32int64System.UInt16int32()、 uint64 () nativeint 和unativeintSystem.UInt64System.IntPtr()。System.UIntPtr
基本浮點類型為 float (System.Double)、 float32 (System.Single) 和 decimal (System.Decimal)。
選擇性寬度是整數,表示結果的最小寬度。 例如, %6d 列印整數,前面加上空格以填滿至少六個字元。 如果 width 是 *,則會採用額外的整數自變數來指定對應的寬度。
有效的旗標為:
| 旗標 | 影響 |
|---|---|
0 |
新增零而不是空格,以構成所需的寬度 |
- |
左對齊指定寬度內的結果 |
+ |
+如果數位為正數,請新增字元(以符合-負號) |
| 空白字元 | 如果數位為正數,請新增額外的空格(以符合負號的 '-' 正負號) |
printf # 旗標無效,如果使用編譯時間錯誤,則會報告錯誤。
值會使用不因文化特性而異來格式化。 文化特性設定與printf格式化無關,除非它們會影響和%A格式設定的結果%O。 如需詳細資訊,請參閱 結構化純文本格式設定。
%A 格式
%A格式規範是用來以人類可讀取的方式格式化值,也可用於報告診斷資訊。
基本值
使用 %A 規範格式化純文本時,F# 數值會以其後綴和不因文化特性而格式化。 浮點值會使用10個浮點精確度的位置來格式化。 例如,
printfn "%A" (1L, 3n, 5u, 7, 4.03f, 5.000000001, 5.0000000001)
生產
(1L, 3n, 5u, 7, 4.03000021f, 5.000000001, 5.0)
使用 %A 規範時,字串會使用引號來格式化。 逸出碼不會新增,而是列印原始字元。 例如,
printfn "%A" ("abc", "a\tb\nc\"d")
生產
("abc", "a b
c"d")
.NET 值
使用%A規範格式化純文字時,非 F# .NET 物件會使用 x.ToString() 和 System.Globalization.CultureInfo.CurrentUICulture所提供的 System.Globalization.CultureInfo.CurrentCulture .NET 預設設定來格式化。 例如,
open System.Globalization
let date = System.DateTime(1999, 12, 31)
CultureInfo.CurrentCulture <- CultureInfo.GetCultureInfo("de-DE")
printfn "Culture 1: %A" date
CultureInfo.CurrentCulture <- CultureInfo.GetCultureInfo("en-US")
printfn "Culture 2: %A" date
生產
Culture 1: 31.12.1999 00:00:00
Culture 2: 12/31/1999 12:00:00 AM
結構化值
使用 %A 規範格式化純文字時,區塊縮排會用於 F# 清單和元組。 如先前的範例所示。
也會使用數位結構,包括多維度陣列。 單一維度陣列會以 [| ... |] 語法顯示。 例如,
printfn "%A" [| for i in 1 .. 20 -> (i, i*i) |]
生產
[|(1, 1); (2, 4); (3, 9); (4, 16); (5, 25); (6, 36); (7, 49); (8, 64); (9, 81);
(10, 100); (11, 121); (12, 144); (13, 169); (14, 196); (15, 225); (16, 256);
(17, 289); (18, 324); (19, 361); (20, 400)|]
默認列印寬度為80。 此寬度可以使用格式規範中的列印寬度來自定義。 例如,
printfn "%10A" [| for i in 1 .. 5 -> (i, i*i) |]
printfn "%20A" [| for i in 1 .. 5 -> (i, i*i) |]
printfn "%50A" [| for i in 1 .. 5 -> (i, i*i) |]
生產
[|(1, 1);
(2, 4);
(3, 9);
(4, 16);
(5, 25)|]
[|(1, 1); (2, 4);
(3, 9); (4, 16);
(5, 25)|]
[|(1, 1); (2, 4); (3, 9); (4, 16); (5, 25)|]
指定 0 的列印寬度會導致不使用列印寬度。 除了輸出中的內嵌字串包含換行符之外,單行文字將會產生。 例如:
printfn "%0A" [| for i in 1 .. 5 -> (i, i*i) |]
printfn "%0A" [| for i in 1 .. 5 -> "abc\ndef" |]
生產
[|(1, 1); (2, 4); (3, 9); (4, 16); (5, 25)|]
[|"abc
def"; "abc
def"; "abc
def"; "abc
def"; "abc
def"|]
深度限制為 4 用於序列 (IEnumerable) 值,其顯示為 seq { ...}。 清單和陣列值會使用100的深度限制。
例如,
printfn "%A" (seq { for i in 1 .. 10 -> (i, i*i) })
生產
seq [(1, 1); (2, 4); (3, 9); (4, 16); ...]
區塊縮排也用於公用記錄和等位值的結構。 例如,
type R = { X : int list; Y : string list }
printfn "%A" { X = [ 1;2;3 ]; Y = ["one"; "two"; "three"] }
生產
{ X = [1; 2; 3]
Y = ["one"; "two"; "three"] }
如果使用 %+A ,則也會使用反映來顯示記錄和等位的私人結構。 例如:
type internal R =
{ X : int list; Y : string list }
override _.ToString() = "R"
let internal data = { X = [ 1;2;3 ]; Y = ["one"; "two"; "three"] }
printfn "external view:\n%A" data
printfn "internal view:\n%+A" data
生產
external view:
R
internal view:
{ X = [1; 2; 3]
Y = ["one"; "two"; "three"] }
大型、迴圈或深層巢狀值
大型結構化值會格式化為最大整體物件節點計數 10000。
深度巢狀值會格式化為100的深度。 在這兩種情況下, ... 都用來將部分輸出滑倒。 例如,
type Tree =
| Tip
| Node of Tree * Tree
let rec make n =
if n = 0 then
Tip
else
Node(Tip, make (n-1))
printfn "%A" (make 1000)
會產生具有某些部分的較大輸出:
Node(Tip, Node(Tip, ....Node (..., ...)...))
迴圈會在物件圖形中偵測到,並 ... 用於偵測到迴圈的位置。 例如:
type R = { mutable Links: R list }
let r = { Links = [] }
r.Links <- [r]
printfn "%A" r
生產
{ Links = [...] }
延遲、Null 和函式值
當尚未評估值時,延遲值會列印為 Value is not created 或對等的文字。
Null 值會列印為 null ,除非值的靜態類型決定為等位型別,其中 null 是允許的表示法。
F# 函式值會列印為其內部產生的關閉名稱,例如 <fun:it@43-7>。
使用 自訂純文字格式設定 StructuredFormatDisplay
使用 %A 規範時,會遵守類型宣告上屬性的存在 StructuredFormatDisplay 。 這可用來指定 Surrogate 文字和屬性來顯示值。 例如:
[<StructuredFormatDisplay("Counts({Clicks})")>]
type Counts = { Clicks:int list}
printfn "%20A" {Clicks=[0..20]}
生產
Counts([0; 1; 2; 3;
4; 5; 6; 7;
8; 9; 10; 11;
12; 13; 14;
15; 16; 17;
18; 19; 20])
覆寫以自定義純文本格式設定 ToString
的預設實 ToString 作可在 F# 程式設計中觀察到。 默認結果通常不適合用於程式設計人員面向信息顯示或用戶輸出,因此通常會覆寫預設實作。
根據預設,F# 記錄和等位型別會使用 的實作覆寫 的 ToString 實 sprintf "%+A"作。 例如,
type Counts = { Clicks:int list }
printfn "%s" ({Clicks=[0..10]}.ToString())
生產
{ Clicks = [0; 1; 2; 3; 4; 5; 6; 7; 8; 9; 10] }
針對類別類型,不會提供 預設的 ToString 實作,而且會使用 .NET 預設值來報告型別的名稱。 例如,
type MyClassType(clicks: int list) =
member _.Clicks = clicks
let data = [ MyClassType([1..5]); MyClassType([1..5]) ]
printfn "Default structured print gives this:\n%A" data
printfn "Default ToString gives:\n%s" (data.ToString())
生產
Default structured print gives this:
[MyClassType; MyClassType]
Default ToString gives:
[MyClassType; MyClassType]
為新增覆寫 ToString 可提供更佳的格式設定。
type MyClassType(clicks: int list) =
member _.Clicks = clicks
override _.ToString() = sprintf "MyClassType(%0A)" clicks
let data = [ MyClassType([1..5]); MyClassType([1..5]) ]
printfn "Now structured print gives this:\n%A" data
printfn "Now ToString gives:\n%s" (data.ToString())
生產
Now structured print gives this:
[MyClassType([1; 2; 3; 4; 5]); MyClassType([1; 2; 3; 4; 5])]
Now ToString gives:
[MyClassType([1; 2; 3; 4; 5]); MyClassType([1; 2; 3; 4; 5])]
使用 StructuredFormatDisplay 和自訂純文字格式設定 ToString
若要達到 和 %O 格式規範的一致格式%A設定,請將 的用法StructuredFormatDisplay與的ToString覆寫結合。 例如,
[<StructuredFormatDisplay("{DisplayText}")>]
type MyRecord =
{
a: int
}
member this.DisplayText = this.ToString()
override _.ToString() = "Custom ToString"
評估下列定義
let myRec = { a = 10 }
let myTuple = (myRec, myRec)
let s1 = sprintf $"{myRec}"
let s2 = sprintf $"{myTuple}"
let s3 = sprintf $"%A{myTuple}"
let s4 = sprintf $"{[myRec; myRec]}"
let s5 = sprintf $"%A{[myRec; myRec]}"
提供文字
val myRec: MyRecord = Custom ToString
val myTuple: MyRecord * MyRecord = (Custom ToString, Custom ToString)
val s1: string = "Custom ToString"
val s2: string = "(Custom ToString, Custom ToString)"
val s3: string = "(Custom ToString, Custom ToString)"
val s4: string = "[Custom ToString; Custom ToString]"
val s5: string = "[Custom ToString; Custom ToString]"
搭配支援屬性使用 StructuredFormatDisplay 表示在結構化列印期間會忽略 結構記錄類型,myRec而且在所有情況下都偏好覆寫 ToString() 。DisplayText
您可以新增 介面的 System.IFormattable 實作,以在 .NET 格式規格存在的情況下進一步自定義。
F# 互動式結構化列印
F# Interactive (dotnet fsi) 使用結構化純文字格式的擴充版本來報告值,並允許額外的自定義性。 如需詳細資訊,請參閱 F# Interactive。
自定義偵錯顯示
.NET 的調試程式會遵守 和 等DebuggerDisplayDebuggerTypeProxy屬性的使用,這些會影響調試程式檢查視窗中對象的結構化顯示。
F# 編譯程式會自動為受歧視的等位和記錄類型產生這些屬性,但不是類別、介面或結構類型。
這些屬性在 F# 純文字格式設定中會被忽略,但是實作這些方法有助於改善偵錯 F# 類型時的顯示。