Partager via


Mise en forme de texte brut

F# prend en charge la mise en forme vérifiée par type de texte brut à l’aide des fonctions associées, printfnsprintfet les printffonctions associées. Par exemple,

dotnet fsi

> printfn "Hello %s, %d + %d is %d" "world" 2 2 (2+2);;

donne la sortie

Hello world, 2 + 2 is 4

F# permet également aux valeurs structurées d’être mises en forme en texte brut. Par exemple, considérez l’exemple suivant qui met en forme la sortie en tant qu’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 de texte brut structuré est activée lorsque vous utilisez le %A format dans les chaînes de mise en printf forme. Il est également activé lors de la mise en forme de la sortie des valeurs dans 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() l’union F# et aux valeurs d’enregistrement, y compris celles qui se produisent implicitement dans le débogage, la journalisation et d’autres outils.

Vérification des printfchaînes -format

Une erreur au moment de la compilation est signalée si une printf fonction de mise en forme 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)

donne la sortie

  sprintf "Hello %s" (2+2)
  ----------------------^

stdin(3,25): error FS0001: The type 'string' does not match the type 'int'

Techniquement, lors de l’utilisation printf et d’autres fonctions associées, une règle spéciale dans le compilateur F# vérifie le littéral de chaîne transmis en tant que chaîne de format, garantissant que les arguments suivants appliqués sont du type correct pour correspondre aux spécificateurs de format utilisés.

Spécificateurs de format pour printf

Les spécifications de format pour printf les formats sont des chaînes avec % des marqueurs qui indiquent le format. Les espaces réservés au format se composent de %[flags][width][.precision][type] l’emplacement où le type est interprété comme suit :

Spécificateur de format Type(s) Remarques
%b bool (System.Boolean) Mise en forme en tant que true ou false
%s string (System.String) Mis en forme comme son contenu non bouclé
%c char (System.Char) Mise en forme en tant que littéral de caractère
%d, %i type entier de base Mise en forme en tant qu’entier décimal, signé si le type 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 numéro octal non signé
%B type entier de base Mis en forme en tant que nombre binaire non signé
%e, %E un type à virgule flottante de base Mis en forme comme valeur signée ayant le formulaire [-]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 un type à virgule flottante de base Mise en forme comme valeur signée ayant le formulaire [-]dddd.dddd, où dddd se trouve 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 la virgule décimale dépend de la précision demandée.
%g, %G un type à virgule flottante de base Mise en forme à l’aide d’une valeur signée imprimée ou %f%e au format, selon le format le plus compact pour la valeur donnée et la précision.
%M a decimal (System.Decimal) valeur Mise en forme à l’aide du spécificateur de "G" format pour System.Decimal.ToString(format)
%O n’importe quelle valeur Mise en forme en boxant l’objet et en appelant sa System.Object.ToString() méthode
%A n’importe quelle valeur Mise en forme à l’aide d’une mise en forme de texte brut structuré avec les paramètres de disposition par défaut
%a n’importe quelle 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 n’importe quelle 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 brut : %

Les types entiers de base sont byte (System.Byte), sbyte (System.SByte), int16 (System.Int16), uint16 (System.UInt16), int32 (), uint32 (System.Int32), (System.UInt32), int64 (System.Int64), (System.UInt64) nativeintuint64System.IntPtret unativeint ().System.UIntPtr Les types à virgule flottante de base sont 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 le préfixant avec des espaces pour remplir au moins six caractères. Si la largeur est *, un argument entier supplémentaire est pris pour spécifier la largeur correspondante.

Les indicateurs valides sont les suivants :

Drapeau Résultat
0 Ajouter des zéros au lieu d’espaces pour créer la largeur requise
- À gauche, justifiez le résultat dans la largeur spécifiée
+ Ajoutez un + caractère si le nombre est positif (pour faire correspondre un - signe pour les négatifs)
caractère d’espace Ajoutez un espace supplémentaire si le nombre est positif (pour correspondre à un signe « - » pour les négatifs)

L’indicateur printf # n’est pas valide et une erreur au moment de la compilation est signalée si elle est utilisée.

Les valeurs sont mises en forme à l’aide d’une culture invariante. Les paramètres de culture ne sont pas pertinents pour printf la mise en forme, sauf lorsqu’ils affectent les résultats et %O%A la mise en forme. Pour plus d’informations, consultez la mise en forme de texte brut structuré.

%A Formatage

Le spécificateur de %A format est utilisé pour mettre en forme les valeurs d’une manière lisible par l’homme et peut également être utile pour signaler des informations de diagnostic.

Valeurs primitives

Lors de la mise en forme du texte brut à l’aide du %A spécificateur, 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 à 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)

Lorsque vous utilisez le %A spécificateur, 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. 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 %A spécificateur, les objets .NET non F# sont mis en forme à l’aide x.ToString() des paramètres par défaut de .NET donné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, la %A mise en retrait de bloc est utilisée pour les listes F# et les tuples. Ceci est illustré dans l’exemple précédent. La structure des tableaux est également utilisée, y compris les tableaux multidimensionnels. Les tableaux unidimensionnels sont affichés avec [| ... |] une 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 par défaut est 80. Cette largeur peut être personnalisée à l’aide d’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)|]

La spécification d’une largeur d’impression de 0 entraîne l’utilisation d’aucune largeur d’impression. Une seule ligne de texte se traduit, 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 sous la forme 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 de 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 elle est utilisée, 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 sur un nombre maximal de nœuds d’objet global de 1 0000. Les valeurs profondément imbriquées sont mises en forme à une profondeur de 100. Dans les deux cas ... , il est utilisé pour éliser 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 grande sortie avec certaines parties supprimées :

Node(Tip, Node(Tip, ....Node (..., ...)...))

Les cycles sont détectés dans les graphiques d’objets et ... sont utilisés à des endroits où les 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, null et de fonction

Les valeurs différées sont imprimées sous forme Value is not created ou texte équivalent lorsque la valeur n’a pas encore été évaluée.

Les valeurs Null sont imprimées comme null sauf si le type statique de la valeur est déterminé comme 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

Lorsque vous utilisez le %A spécificateur, la présence de l’attribut StructuredFormatDisplay sur les déclarations de type est respectée. Cela peut être utilisé pour spécifier le texte et la propriété de substitution 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 remplaçant ToString

L’implémentation par défaut est ToString observable dans la programmation F#. Souvent, les résultats par défaut ne conviennent pas à une utilisation dans l’affichage des informations orientées programmeur ou la sortie utilisateur, et par conséquent, il est courant de remplacer l’implémentation par défaut.

Par défaut, les types d’enregistrement et d’union F# remplacent l’implémentation d’une ToString 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 n’est ToString fournie et la valeur par défaut .NET est utilisée, ce qui indique 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 ToString peut donner 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 pour %A et %O des spécificateurs de format, combinez l’utilisation avec StructuredFormatDisplay 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 la propriété de prise en charge DisplayText signifie que le myRec type d’enregistrement structurel est ignoré lors de StructuredFormatDisplay l’impression structurée et que le remplacement est ToString() préféré dans toutes les circonstances.

Une implémentation de l’interface System.IFormattable peut être ajoutée pour une personnalisation supplémentaire en présence de spécifications de format .NET.

Impression structurée F# Interactive

F# Interactive (dotnet fsi) utilise une version étendue de la mise en forme de texte brut structuré pour signaler des 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 tels que DebuggerDisplay et DebuggerTypeProxy, et ils affectent l’affichage structuré des objets dans les fenêtres d’inspection du débogueur. Le compilateur F# a généré automatiquement ces attributs pour les types d’enregistrements et d’union discriminatoires, mais pas les types de classe, d’interface ou de struct.

Ces attributs sont ignorés dans la mise en forme de texte brut F#, mais il peut être utile d’implémenter ces méthodes pour améliorer les affichages lors du débogage des types F#.

Voir aussi