Mise en forme en texte brut
F# prend en charge la mise en forme vérifiée par type du texte brut à l’aide de printf
, printfn
, sprintf
et des fonctions associées.
Par exemple,
dotnet fsi
> printfn "Hello %s, %d + %d is %d" "world" 2 2 (2+2);;
génère la sortie
Hello world, 2 + 2 is 4
F# permet également de mettre en forme des valeurs structurées en texte brut. L’exemple suivant met en forme la sortie sous la forme d’un affichage de tuples de type matrice.
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)]]
La mise en forme structurée de texte brut est activée quand vous utilisez le format %A
dans les chaînes de mise en forme printf
.
Elle est également activée lors de la mise en forme de la sortie des valeurs en F# Interactive, où la sortie inclut des informations supplémentaires et est également personnalisable.
La mise en forme de texte brut est également observable via tous les appels à x.ToString()
sur les valeurs d’union et d’enregistrement F#, notamment celles qui se produisent implicitement dans le débogage, la journalisation et d’autres outils.
Vérification des chaînes au format printf
Une erreur de compilation est signalée si une fonction de mise en forme printf
est utilisée avec un argument qui ne correspond pas aux spécificateurs de format printf dans la chaîne de format. Par exemple,
sprintf "Hello %s" (2+2)
génère la sortie
sprintf "Hello %s" (2+2)
----------------------^
stdin(3,25): error FS0001: The type 'string' does not match the type 'int'
Techniquement parlant, quand vous utilisez printf
et d’autres fonctions associées, une règle spéciale du compilateur F# vérifie le littéral de chaîne transmis en tant que chaîne de format. Cela permet de s’assurer que les arguments suivants appliqués sont du type approprié pour correspondre aux spécificateurs de format utilisés.
Spécificateurs de format pour printf
Les spécifications de format pour les formats printf
sont des chaînes avec des marqueurs %
qui indiquent le format. Les espaces réservés de format se composent de %[flags][width][.precision][type]
où le type est interprété comme suit :
Spécificateur de format | Type(s) | Notes |
---|---|---|
%b |
bool (System.Boolean ) |
Mis en forme en tant que true ou false |
%s |
string (System.String ) |
Mise en forme en tant que contenu sans séquence d’échappement |
%c |
char (System.Char ) |
Mise en forme en tant que littéral de caractère |
%d , %i |
type entier de base | Mis en forme comme un entier décimal, signé si le type d'entier de base est signé |
%u |
type entier de base | Mise en forme en tant qu’entier décimal non signé |
%x , %X |
type entier de base | Mise en forme en tant que nombre hexadécimal non signé (a-f ou A-F pour les chiffres hexadécimaux respectivement) |
%o |
type entier de base | Mise en forme en tant que nombre octal non signé |
%B |
type entier de base | Mise en forme en tant que nombre binaire non signé |
%e , %E |
type à virgule flottante de base | Mise en forme en tant que valeur signée au format [-]d.dddde[sign]ddd où d est un chiffre décimal unique, dddd est un ou plusieurs chiffres décimaux, ddd est exactement trois chiffres décimaux et le signe est + ou - |
%f , %F |
type à virgule flottante de base | Mise en forme en tant que valeur signée au format [-]dddd.dddd , où dddd est un ou plusieurs chiffres décimaux. Le nombre de chiffres avant la virgule décimale dépend de l'ampleur du nombre, et le nombre de chiffres après que la virgule décimale dépend de la précision demandée. |
%g , %G |
type à virgule flottante de base | Mise en forme en tant que valeur signée imprimée au format %f ou %e , selon celui qui est le plus compact pour la valeur et la précision données. |
%M |
Valeur de type decimal (System.Decimal ) |
Mis en forme à l’aide du spécificateur de format "G" pour System.Decimal.ToString(format) |
%O |
toute valeur | Mise en forme à l’aide du boxing de l’objet et l’appel à sa méthode System.Object.ToString() |
%A |
toute valeur | Mise en forme à l’aide de la mise en forme structurée de texte brut avec les paramètres de disposition par défaut |
%a |
toute valeur | Nécessite deux arguments : une fonction de mise en forme acceptant un paramètre de contexte et la valeur, et la valeur particulière à imprimer |
%t |
toute valeur | Nécessite un argument : une fonction de mise en forme acceptant un paramètre de contexte qui génère ou retourne le texte approprié |
%% |
(aucun) | Ne nécessite aucun argument et imprime un signe de pourcentage simple : % |
Voici les types entiers de base : 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
) et unativeint
(System.UIntPtr
).
Voici les types à virgule flottante de base : float
(System.Double
), float32
(System.Single
) et decimal
(System.Decimal
).
La largeur facultative est un entier indiquant la largeur minimale du résultat. Par exemple, %6d
imprime un entier, en ajoutant en préfixe des espaces pour remplir au moins six caractères. Si width est définie sur *
, un argument entier supplémentaire est pris pour spécifier la largeur correspondante.
Voici les indicateurs valides :
Indicateur | Effet |
---|---|
0 |
Ajouter des zéros à la place des espaces pour constituer la largeur requise |
- |
Justifier à gauche le résultat dans le cadre de la largeur spécifiée |
+ |
Ajouter un caractère + si le nombre est positif (un signe - correspond aux nombres négatifs) |
Espace | Ajouter un caractère supplémentaire si le nombre est positif (un signe « - » correspond aux nombres négatifs) |
L’indicateur printf #
n’est pas valide et une erreur de compilation est signalée s’il est utilisé.
Les valeurs sont mises en forme à l’aide d’une culture invariante. Les paramètres de culture ne sont pas pertinents pour la mise en forme printf
, sauf s’ils affectent les résultats de mise en forme %O
et %A
. Pour plus d’informations, consultez Mise en forme structurée de texte brut.
Mise en forme %A
Le spécificateur de format %A
est utilisé pour mettre en forme des valeurs de manière lisible par les êtres humains. Il est également utile pour la création d’informations de diagnostic.
Valeurs primitives
Lors de la mise en forme du texte brut à l’aide du spécificateur %A
, les valeurs numériques F# sont mises en forme avec leur suffixe et leur culture invariante. Les valeurs à virgule flottante sont mises en forme à l’aide de 10 emplacements de précision de virgule flottante. Par exemple,
printfn "%A" (1L, 3n, 5u, 7, 4.03f, 5.000000001, 5.0000000001)
produit
(1L, 3n, 5u, 7, 4.03000021f, 5.000000001, 5.0)
Quand vous utilisez le spécificateur %A
, les chaînes sont mises en forme à l’aide de guillemets. Les codes d’échappement ne sont pas ajoutés et les caractères bruts sont imprimés à la place. Par exemple,
printfn "%A" ("abc", "a\tb\nc\"d")
produit
("abc", "a b
c"d")
Valeurs .NET
Lors de la mise en forme du texte brut à l’aide du spécificateur %A
, les objets .NET non F# sont mis en forme à l’aide de x.ToString()
avec les paramètres par défaut de .NET déterminés par System.Globalization.CultureInfo.CurrentCulture
et System.Globalization.CultureInfo.CurrentUICulture
. Par exemple,
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
produit
Culture 1: 31.12.1999 00:00:00
Culture 2: 12/31/1999 12:00:00 AM
Valeurs structurées
Lors de la mise en forme du texte brut à l’aide du spécificateur %A
, la mise en retrait en bloc est utilisée pour les listes et les tuples F#. Cette caractéristique apparaît dans l'exemple précédent.
La structure des tableaux est également utilisée, notamment les tableaux multidimensionnels. Les tableaux unidimensionnels sont affichés avec la syntaxe [| ... |]
. Par exemple,
printfn "%A" [| for i in 1 .. 20 -> (i, i*i) |]
produit
[|(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)|]
La largeur d’impression est définie par défaut sur 80. Cette largeur peut être personnalisée via une largeur d’impression dans le spécificateur de format. Par exemple,
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) |]
produit
[|(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)|]
Si vous spécifiez une largeur d’impression sur 0, aucune largeur d’impression n’est utilisée. Le résultat comprend une ligne de texte unique, sauf si les chaînes incorporées dans la sortie contiennent des sauts de ligne. Par exemple
printfn "%0A" [| for i in 1 .. 5 -> (i, i*i) |]
printfn "%0A" [| for i in 1 .. 5 -> "abc\ndef" |]
produit
[|(1, 1); (2, 4); (3, 9); (4, 16); (5, 25)|]
[|"abc
def"; "abc
def"; "abc
def"; "abc
def"; "abc
def"|]
Une limite de profondeur de 4 est utilisée pour les valeurs de séquence (IEnumerable
) qui sont affichées en tant que seq { ...}
. Une limite de profondeur de 100 est utilisée pour les valeurs de liste et de tableau.
Par exemple,
printfn "%A" (seq { for i in 1 .. 10 -> (i, i*i) })
produit
seq [(1, 1); (2, 4); (3, 9); (4, 16); ...]
La mise en retrait en bloc est également utilisée pour la structure des valeurs d’enregistrement public et d’union. Par exemple,
type R = { X : int list; Y : string list }
printfn "%A" { X = [ 1;2;3 ]; Y = ["one"; "two"; "three"] }
produit
{ X = [1; 2; 3]
Y = ["one"; "two"; "three"] }
Si %+A
est utilisé, la structure privée des enregistrements et des unions est également révélée à l’aide de la réflexion. Par exemple
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
produit
external view:
R
internal view:
{ X = [1; 2; 3]
Y = ["one"; "two"; "three"] }
Valeurs volumineuses, cycliques ou profondément imbriquées
Les valeurs structurées volumineuses sont mises en forme avec un total de nombre de nœuds d’objets maximal de 10 000.
Les valeurs profondément imbriquées sont mises en forme à une profondeur de 100. Dans les deux cas, ...
est utilisé pour omettre une partie de la sortie. Par exemple,
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)
produit une sortie volumineuse avec certaines parties supprimées :
Node(Tip, Node(Tip, ....Node (..., ...)...))
Les cycles sont détectés dans les graphiques d’objets et ...
est utilisé quand des cycles sont détectés. Par exemple
type R = { mutable Links: R list }
let r = { Links = [] }
r.Links <- [r]
printfn "%A" r
produit
{ Links = [...] }
Valeurs différées, nulles et de fonction
Les valeurs différées sont imprimées en tant que Value is not created
ou texte équivalent quand la valeur n’a pas encore été évaluée.
Les valeurs nulles sont imprimées en tant que null
, sauf si le type statique de la valeur est déterminé comme étant un type d’union où null
est une représentation autorisée.
Les valeurs de fonction F# sont imprimées en tant que nom de fermeture généré en interne, par exemple <fun:it@43-7>
.
Personnaliser la mise en forme de texte brut avec StructuredFormatDisplay
Lors de l’utilisation du spécificateur %A
, la présence de l’attribut StructuredFormatDisplay
sur les déclarations de type est respectée. Il permet de spécifier le texte de substitution et la propriété pour afficher une valeur. Par exemple :
[<StructuredFormatDisplay("Counts({Clicks})")>]
type Counts = { Clicks:int list}
printfn "%20A" {Clicks=[0..20]}
produit
Counts([0; 1; 2; 3;
4; 5; 6; 7;
8; 9; 10; 11;
12; 13; 14;
15; 16; 17;
18; 19; 20])
Personnaliser la mise en forme de texte brut en écrasant ToString
L’implémentation par défaut de ToString
est observable dans la programmation F#. Les résultats par défaut ne sont généralement pas adaptés à une utilisation dans les informations affichées au programmeur ni dans la sortie utilisateur. L’implémentation par défaut est donc souvent écrasée.
Par défaut, les types d’enregistrement F# et d’union remplacent l’implémentation de ToString
par une implémentation qui utilise sprintf "%+A"
. Par exemple,
type Counts = { Clicks:int list }
printfn "%s" ({Clicks=[0..10]}.ToString())
produit
{ Clicks = [0; 1; 2; 3; 4; 5; 6; 7; 8; 9; 10] }
Pour les types de classes, aucune implémentation par défaut de ToString
n’est fournie et la valeur .NET par défaut est utilisée, qui signale le nom du type. Par exemple,
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())
produit
Default structured print gives this:
[MyClassType; MyClassType]
Default ToString gives:
[MyClassType; MyClassType]
L’ajout d’un remplacement pour ToString
permet d’obtenir une meilleure mise en forme.
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())
produit
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])]
Personnaliser la mise en forme de texte brut avec StructuredFormatDisplay
et ToString
Pour obtenir une mise en forme cohérente des spécificateurs de format %A
et %O
, combinez l’utilisation de StructuredFormatDisplay
et un remplacement de ToString
. Par exemple,
[<StructuredFormatDisplay("{DisplayText}")>]
type MyRecord =
{
a: int
}
member this.DisplayText = this.ToString()
override _.ToString() = "Custom ToString"
Évaluation des définitions suivantes
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]}"
donne le texte
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]"
L’utilisation de StructuredFormatDisplay
avec la propriété DisplayText
annexe signifie que myRec
est un type d’enregistrement structurel qui est ignoré lors de l’impression structurée et que le remplacement de ToString()
est préférable dans toutes les circonstances.
Une implémentation de l’interface System.IFormattable
peut être ajoutée pour augmenter le niveau de personnalisation en présence de spécifications au format .NET.
Impression structurée en F# Interactive
F# Interactive (dotnet fsi
) utilise une version étendue de la mise en forme structurée de texte brut pour signaler les valeurs et permet une personnalisation supplémentaire. Pour plus d’informations, consultez F# Interactive.
Personnaliser les affichages de débogage
Les débogueurs pour .NET respectent l’utilisation d’attributs comme DebuggerDisplay
et DebuggerTypeProxy
. Ceux-ci affectent l’affichage structuré des objets dans les fenêtres d’inspection du débogueur.
Le compilateur F# a généré ces attributs automatiquement pour les types d’enregistrement et d’union discriminés, mais pas pour les types de classe, d’interface ni struct.
Ces attributs sont ignorés dans la mise en forme de texte brut F#. Toutefois, il peut être utile d’implémenter ces méthodes pour améliorer les affichages lors du débogage des types F#.