Oformaterad textformatering
F# stöder typkontrollerad formatering av oformaterad text med hjälp av printf
, printfn
, sprintf
och relaterade funktioner.
Exempel:
dotnet fsi
> printfn "Hello %s, %d + %d is %d" "world" 2 2 (2+2);;
ger utdata
Hello world, 2 + 2 is 4
F# tillåter också att strukturerade värden formateras som oformaterad text. Tänk dig till exempel följande exempel som formaterar utdata som en matrisliknande visning av tupplar.
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)]]
Strukturerad oformaterad textformatering aktiveras när du använder %A
formatet i printf
formateringssträngar.
Det aktiveras också när du formaterar utdata från värden i interaktiv F# där utdata innehåller extra information och dessutom kan anpassas.
Oformaterad textformatering kan också observeras genom alla anrop till x.ToString()
på F#-union- och postvärden, inklusive de som sker implicit vid felsökning, loggning och andra verktyg.
Kontroll av printf
strängar i -format
Ett kompileringsfel rapporteras om en printf
formateringsfunktion används med ett argument som inte matchar formatspecificerarna i formatsträngen. Exempel:
sprintf "Hello %s" (2+2)
ger utdata
sprintf "Hello %s" (2+2)
----------------------^
stdin(3,25): error FS0001: The type 'string' does not match the type 'int'
Tekniskt sett, när du använder printf
och andra relaterade funktioner, kontrollerar en särskild regel i F#-kompilatorn strängliteralen som skickas som formatsträngen, vilket säkerställer att de efterföljande argumenten som tillämpas är av rätt typ för att matcha formatspecificerarna som används.
Formatera specificerare för printf
Formatspecifikationer för printf
format är strängar med %
markörer som anger format. Platshållarna för format består av %[flags][width][.precision][type]
där typen tolkas på följande sätt:
Formatspecificerare | Typ(er) | Kommentarer |
---|---|---|
%b |
bool (System.Boolean ) |
Formaterad som true eller false |
%s |
string (System.String ) |
Formaterat som dess ej inkapslade innehåll |
%c |
char (System.Char ) |
Formaterad som teckenliteral |
%d , %i |
en grundläggande heltalstyp | Formaterat som ett decimal heltal, signerat om den grundläggande heltalstypen är signerad |
%u |
en grundläggande heltalstyp | Formaterat som ett intesignerat decimal heltal |
%x , %X |
en grundläggande heltalstyp | Formaterat som ett osignerat hexadecimalt tal (a-f eller A-F för hexsiffror respektive) |
%o |
en grundläggande heltalstyp | Formaterat som ett osignerat oktalt tal |
%B |
en grundläggande heltalstyp | Formaterat som ett osignerat binärt tal |
%e , %E |
en grundläggande flyttalstyp | Formaterat som ett signerat värde med formuläret [-]d.dddde[sign]ddd där d är en enda decimalsiffra, dddd är en eller flera decimaler, ddd är exakt tre decimaltal och tecken är + eller - |
%f , %F |
en grundläggande flyttalstyp | Formaterat som ett signerat värde med formuläret [-]dddd.dddd , där dddd är en eller flera decimalsiffror. Antalet siffror före decimaltecknet beror på talets storlek och antalet siffror efter decimaltecknet beror på den begärda precisionen. |
%g , %G |
en grundläggande flyttalstyp | Formaterat med ett signerat värde som skrivs ut i %f eller %e format, beroende på vilket som är mer kompakt för det angivna värdet och precisionen. |
%M |
a decimal (System.Decimal ) värde |
Formaterad med formatspecificeraren "G" för System.Decimal.ToString(format) |
%O |
valfritt värde | Formaterad genom att boxa objektet och anropa dess System.Object.ToString() metod |
%A |
valfritt värde | Formaterad med strukturerad oformaterad textformatering med standardlayoutinställningarna |
%a |
valfritt värde | Kräver två argument: en formateringsfunktion som accepterar en kontextparameter och värdet och det specifika värde som ska skrivas ut |
%t |
valfritt värde | Kräver ett argument: en formateringsfunktion som accepterar en kontextparameter som antingen matar ut eller returnerar lämplig text |
%% |
(inget) | Kräver inga argument och skriver ut ett vanligt procenttecken: % |
Grundläggande heltalstyper är byte
(System.Byte
), sbyte
(System.SByte
), int16
(System.Int16
), (), uint16
(System.UInt16
), int32
(System.Int32
), (), uint32
(System.UInt32
), int64
(System.Int64
), uint64
(System.UInt64
), nativeint
(System.IntPtr
) och unativeint
(System.UIntPtr
).
Grundläggande flyttalstyper är float
(System.Double
), float32
(System.Single
) och decimal
(System.Decimal
).
Den valfria bredden är ett heltal som anger resultatets minimala bredd. Skriver till exempel %6d
ut ett heltal och prefixerar det med blanksteg för att fylla minst sex tecken. Om bredden är *
används ett extra heltalsargument för att ange motsvarande bredd.
Giltiga flaggor är:
Flagga | Effekt |
---|---|
0 |
Lägg till nollor i stället för blanksteg för att skapa den bredd som krävs |
- |
Vänsterjustera resultatet inom den angivna bredden |
+ |
Lägg till ett + tecken om talet är positivt (för att matcha ett - tecken på negativa värden) |
blankstegstecken | Lägg till ett extra utrymme om talet är positivt (för att matcha ett "-"-tecken för negativa värden) |
Utskriftsflaggan #
är ogiltig och ett kompileringsfel rapporteras om den används.
Värden formateras med hjälp av invariant kultur. Kulturinställningar är irrelevanta printf
för formatering, förutom när de påverkar resultatet av %O
och %A
formateringen. Mer information finns i formaterad oformaterad text.
%A
Formatering
Formatspecificeraren %A
används för att formatera värden på ett sätt som kan läsas av människor och kan också vara användbart för rapportering av diagnostikinformation.
Primitiva värden
När du formaterar oformaterad text med hjälp av %A
specificeraren formateras numeriska F#-värden med deras suffix och invarianta kultur. Flyttalsvärden formateras med 10 flyttalsprecisionsplatser. Exempel:
printfn "%A" (1L, 3n, 5u, 7, 4.03f, 5.000000001, 5.0000000001)
Producerar
(1L, 3n, 5u, 7, 4.03000021f, 5.000000001, 5.0)
När du använder specificeraren %A
formateras strängar med citattecken. Escape-koder läggs inte till och i stället skrivs de råa tecknen ut. Exempel:
printfn "%A" ("abc", "a\tb\nc\"d")
Producerar
("abc", "a b
c"d")
.NET-värden
När du formaterar oformaterad text med hjälp %A
av specificeraren formateras .NET-objekt som inte är F# med hjälp x.ToString()
av standardinställningarna för .NET som anges av System.Globalization.CultureInfo.CurrentCulture
och System.Globalization.CultureInfo.CurrentUICulture
. Exempel:
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
Producerar
Culture 1: 31.12.1999 00:00:00
Culture 2: 12/31/1999 12:00:00 AM
Strukturerade värden
När du formaterar oformaterad text med hjälp av %A
specificeraren används block indrag för F#-listor och tupplar. Detta visas i föregående exempel.
Matrisernas struktur används också, inklusive flerdimensionella matriser. Endimensionella matriser visas med [| ... |]
syntax. Exempel:
printfn "%A" [| for i in 1 .. 20 -> (i, i*i) |]
Producerar
[|(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)|]
Standardbredden för utskrift är 80. Den här bredden kan anpassas med hjälp av en utskriftsbredd i formatspecificeraren. Exempel:
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) |]
Producerar
[|(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)|]
Om du anger utskriftsbredden 0 används ingen utskriftsbredd. En enda textrad resulterar, förutom där inbäddade strängar i utdata innehåller radbrytningar. Till exempel
printfn "%0A" [| for i in 1 .. 5 -> (i, i*i) |]
printfn "%0A" [| for i in 1 .. 5 -> "abc\ndef" |]
Producerar
[|(1, 1); (2, 4); (3, 9); (4, 16); (5, 25)|]
[|"abc
def"; "abc
def"; "abc
def"; "abc
def"; "abc
def"|]
En djupgräns på 4 används för sekvensvärden (IEnumerable
) som visas som seq { ...}
. En djupgräns på 100 används för list- och matrisvärden.
Exempel:
printfn "%A" (seq { for i in 1 .. 10 -> (i, i*i) })
Producerar
seq [(1, 1); (2, 4); (3, 9); (4, 16); ...]
Block indrag används också för strukturen för offentliga poster och fackliga värden. Exempel:
type R = { X : int list; Y : string list }
printfn "%A" { X = [ 1;2;3 ]; Y = ["one"; "two"; "three"] }
Producerar
{ X = [1; 2; 3]
Y = ["one"; "two"; "three"] }
Om %+A
används avslöjas också den privata strukturen för poster och fackföreningar med hjälp av reflektion. Till exempel
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
Producerar
external view:
R
internal view:
{ X = [1; 2; 3]
Y = ["one"; "two"; "three"] }
Stora, cykliska eller djupt kapslade värden
Stora strukturerade värden formateras till ett maximalt totalt antal objektnoder på 1 0000.
Djupt kapslade värden formateras till ett djup på 100. I båda fallen ...
används för att elide några av utdata. Exempel:
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)
genererar stora utdata med vissa delar elide:
Node(Tip, Node(Tip, ....Node (..., ...)...))
Cykler identifieras i objektdiagram och ...
används på platser där cykler identifieras. Till exempel
type R = { mutable Links: R list }
let r = { Links = [] }
r.Links <- [r]
printfn "%A" r
Producerar
{ Links = [...] }
Lata värden, null- och funktionsvärden
Lata värden skrivs ut som Value is not created
eller motsvarande text när värdet ännu inte har utvärderats.
Null-värden skrivs ut som null
om inte den statiska typen av värdet bestäms vara en unionstyp där null
är en tillåten representation.
F#-funktionsvärden skrivs ut som deras internt genererade stängningsnamn, till exempel <fun:it@43-7>
.
Anpassa oformaterad textformatering med StructuredFormatDisplay
När du använder %A
specificeraren respekteras förekomsten av StructuredFormatDisplay
attributet för typdeklarationer. Detta kan användas för att ange surrogattext och egenskap för att visa ett värde. Till exempel:
[<StructuredFormatDisplay("Counts({Clicks})")>]
type Counts = { Clicks:int list}
printfn "%20A" {Clicks=[0..20]}
Producerar
Counts([0; 1; 2; 3;
4; 5; 6; 7;
8; 9; 10; 11;
12; 13; 14;
15; 16; 17;
18; 19; 20])
Anpassa oformaterad textformatering genom att åsidosätta ToString
Standardimplementeringen av ToString
kan observeras i F#-programmering. Standardresultaten är ofta inte lämpliga för användning i antingen programmerriktad informationsvisning eller användarutdata, och därför är det vanligt att åsidosätta standardimplementeringen.
Som standard åsidosätter F#-post- och unionstyper implementeringen av ToString
med en implementering som använder sprintf "%+A"
. Exempel:
type Counts = { Clicks:int list }
printfn "%s" ({Clicks=[0..10]}.ToString())
Producerar
{ Clicks = [0; 1; 2; 3; 4; 5; 6; 7; 8; 9; 10] }
För klasstyper tillhandahålls ingen standardimplementering av ToString
och .NET-standardvärdet används, vilket rapporterar namnet på typen. Exempel:
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())
Producerar
Default structured print gives this:
[MyClassType; MyClassType]
Default ToString gives:
[MyClassType; MyClassType]
Att lägga till en åsidosättning för ToString
kan ge bättre formatering.
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())
Producerar
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])]
Anpassa oformaterad textformatering med StructuredFormatDisplay
och ToString
Om du vill uppnå konsekvent formatering för %A
och %O
formatspecificerare kombinerar du användningen av StructuredFormatDisplay
med en åsidosättning av ToString
. Exempel:
[<StructuredFormatDisplay("{DisplayText}")>]
type MyRecord =
{
a: int
}
member this.DisplayText = this.ToString()
override _.ToString() = "Custom ToString"
Utvärdera följande definitioner
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]}"
ger texten
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]"
Användningen av StructuredFormatDisplay
med den stödjande DisplayText
egenskapen innebär att myRec
är en strukturell posttyp ignoreras under strukturerad utskrift, och åsidosättningen av ToString()
föredras under alla omständigheter.
En implementering av System.IFormattable
gränssnittet kan läggas till för ytterligare anpassning i närvaro av .NET-formatspecifikationer.
Interaktiv strukturerad utskrift i F#
F# Interactive (dotnet fsi
) använder en utökad version av strukturerad oformaterad textformatering för att rapportera värden och möjliggör ytterligare anpassning. Mer information finns i F# Interactive.
Anpassa felsökningsskärmar
Felsökningsprogram för .NET respekterar användningen av attribut som DebuggerDisplay
och DebuggerTypeProxy
, och dessa påverkar den strukturerade visningen av objekt i felsökningsgranskningsfönster.
F#-kompilatorn genererade automatiskt dessa attribut för diskriminerade union- och posttyper, men inte klass-, gränssnitts- eller struct-typer.
Dessa attribut ignoreras i Oformaterad F#-formatering, men det kan vara användbart att implementera dessa metoder för att förbättra visning vid felsökning av F#-typer.