Compartir a través de


Formato de texto sin formato

F# admite el formato con comprobación de tipos de texto sin formato mediante printffunciones relacionadas , printfn, sprintfy . Por ejemplo

dotnet fsi

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

proporciona la salida

Hello world, 2 + 2 is 4

F# también permite dar formato a los valores estructurados como texto sin formato. Por ejemplo, considere el ejemplo siguiente que da formato a la salida como una presentación similar a una matriz de tuplas.

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)]]

El formato de texto sin formato estructurado se activa cuando se usa el %A formato en printf cadenas de formato. También se activa al dar formato a la salida de valores en F# interactivo, donde la salida incluye información adicional y además se puede personalizar. El formato de texto sin formato también es observable a través de las llamadas a x.ToString() en valores de unión y registro de F#, incluidos los que se producen implícitamente en la depuración, el registro y otras herramientas.

Comprobación de cadenas de printfformato

Se notificará un error en tiempo de compilación si se usa una printf función de formato con un argumento que no coincide con los especificadores de formato printf en la cadena de formato. Por ejemplo

sprintf "Hello %s" (2+2)

proporciona la salida

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

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

Técnicamente, al usar printf y otras funciones relacionadas, una regla especial en el compilador de F# comprueba el literal de cadena pasado como cadena de formato, lo que garantiza que los argumentos posteriores aplicados son del tipo correcto para que coincida con los especificadores de formato usados.

Especificadores de formato para printf

Las especificaciones de formato para printf los formatos son cadenas con % marcadores que indican el formato. Los marcadores de posición de formato constan de %[flags][width][.precision][type] dónde se interpreta el tipo de la siguiente manera:

Especificador de formato Tipos Observaciones
%b bool (System.Boolean) Con formato o truefalse
%s string (System.String) Con formato de contenido sin escape
%c char (System.Char) Con formato como literal de carácter
%d, %i un tipo entero básico Con formato de entero decimal, con signo si el tipo entero básico tiene signo
%u un tipo entero básico Con formato de entero decimal sin signo
%x, %X un tipo entero básico Con formato como un número hexadecimal sin signo (a-f o A-F para dígitos hexadecimales respectivamente)
%o un tipo entero básico Con formato de número octal sin signo
%B un tipo entero básico Con formato de número binario sin signo
%e, %E un tipo de punto flotante básico Con formato de valor con signo que tiene el formato [-]d.dddde[sign]ddd donde d es un solo dígito decimal, dddd es uno o varios dígitos decimales, ddd es exactamente tres dígitos decimales y el signo es + o -
%f, %F un tipo de punto flotante básico Con formato como un valor con signo que tiene el formulario [-]dddd.dddd, donde dddd es uno o varios dígitos decimales. El número de dígitos antes del separador decimal depende de la magnitud del número y del número de dígitos después del separador decimal depende de la precisión solicitada.
%g, %G un tipo de punto flotante básico Con formato utilizando como un valor con signo impreso en %f formato o %e , lo que sea más compacto para el valor y la precisión especificados.
%M un decimal valor (System.Decimal) Con formato mediante el "G" especificador de formato para System.Decimal.ToString(format)
%O cualquier valor Formateado mediante la conversión boxing del objeto y la llamada a su System.Object.ToString() método
%A cualquier valor Formato con formato de texto sin formato estructurado con la configuración de diseño predeterminada
%a cualquier valor Requiere dos argumentos: una función de formato que acepta un parámetro de contexto y el valor, y el valor concreto que se va a imprimir.
%t cualquier valor Requiere un argumento: una función de formato que acepta un parámetro de contexto que genera o devuelve el texto adecuado.
%% (ninguno) No requiere argumentos e imprime un signo de porcentaje sin formato: %

Los tipos enteros básicos son byte (), (System.SByteSystem.Byte), sbyte (), int16 (System.Int16), uint16 (System.UInt16), int32 (System.Int32), uint32 (System.UInt32), int64 (System.Int64), uint64 (), nativeint (System.UInt64System.IntPtr) y unativeint (System.UIntPtr). Los tipos de punto flotante básicos son float (System.Double), float32 (System.Single) y decimal (System.Decimal).

El ancho opcional es un entero que indica el ancho mínimo del resultado. Por ejemplo, %6d imprime un entero, prefijo con espacios para rellenar al menos seis caracteres. Si width es *, se toma un argumento entero adicional para especificar el ancho correspondiente.

Las marcas válidas son:

Bandera Efecto
0 Agregar ceros en lugar de espacios para componer el ancho necesario
- La izquierda justifica el resultado dentro del ancho especificado
+ Agregue un + carácter si el número es positivo (para que coincida con un - signo para los negativos)
carácter de espacio Agregue un espacio adicional si el número es positivo (para que coincida con un signo "-" para los negativos)

La marca printf # no es válida y se notificará un error en tiempo de compilación si se usa.

Los valores tienen el formato de referencia cultural invariable. La configuración de referencia cultural es irrelevante para dar printf formato, excepto cuando afectan a los resultados y %O%A al formato. Para obtener más información, consulte formato de texto sin formato estructurado.

%A formateo

El %A especificador de formato se usa para dar formato a los valores de forma legible y también puede ser útil para notificar información de diagnóstico.

Valores primitivos

Al aplicar formato al texto sin formato mediante el %A especificador, los valores numéricos de F# tienen formato con sufijo y referencia cultural invariable. Los valores de punto flotante tienen el formato de 10 posiciones de precisión de punto flotante. Por ejemplo

printfn "%A" (1L, 3n, 5u, 7, 4.03f, 5.000000001, 5.0000000001)

Produce

(1L, 3n, 5u, 7, 4.03000021f, 5.000000001, 5.0)

Cuando se usa el %A especificador, las cadenas tienen formato entre comillas. Los códigos de escape no se agregan y, en su lugar, se imprimen los caracteres sin formato. Por ejemplo

printfn "%A" ("abc", "a\tb\nc\"d")

Produce

("abc", "a      b
c"d")

Valores de .NET

Al aplicar formato a texto sin formato mediante el %A especificador, los objetos .NET que no son de F# tienen formato mediante x.ToString() la configuración predeterminada de .NET dada por System.Globalization.CultureInfo.CurrentCulture y System.Globalization.CultureInfo.CurrentUICulture. Por ejemplo

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

Produce

Culture 1: 31.12.1999 00:00:00
Culture 2: 12/31/1999 12:00:00 AM

Valores estructurados

Al aplicar formato a texto sin formato mediante el %A especificador, la sangría de bloques se usa para listas y tuplas de F#. Esto se muestra en el ejemplo anterior. También se usa la estructura de matrices, incluidas matrices multidimensionales. Las matrices unidimensionales se muestran con [| ... |] sintaxis. Por ejemplo

printfn "%A" [| for i in 1 .. 20 -> (i, i*i) |]

Produce

[|(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)|]

El ancho de impresión predeterminado es 80. Este ancho se puede personalizar mediante un ancho de impresión en el especificador de formato. Por ejemplo

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) |]

Produce

[|(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 se especifica un ancho de impresión de 0, no se usa ningún ancho de impresión. Se producirá una sola línea de texto, excepto cuando las cadenas incrustadas de la salida contengan saltos de línea. Por ejemplo

printfn "%0A" [| for i in 1 .. 5 -> (i, i*i) |]

printfn "%0A" [| for i in 1 .. 5 -> "abc\ndef" |]

Produce

[|(1, 1); (2, 4); (3, 9); (4, 16); (5, 25)|]
[|"abc
def"; "abc
def"; "abc
def"; "abc
def"; "abc
def"|]

Se usa un límite de profundidad de 4 para los valores de secuencia (IEnumerable), que se muestran como seq { ...}. Se usa un límite de profundidad de 100 para los valores de lista y matriz. Por ejemplo

printfn "%A" (seq { for i in 1 .. 10 -> (i, i*i) })

Produce

seq [(1, 1); (2, 4); (3, 9); (4, 16); ...]

La sangría de bloques también se usa para la estructura de los valores de registro público y unión. Por ejemplo

type R = { X : int list; Y : string list }

printfn "%A" { X =  [ 1;2;3 ]; Y = ["one"; "two"; "three"] }

Produce

{ X = [1; 2; 3]
  Y = ["one"; "two"; "three"] }

Si %+A se usa, la estructura privada de registros y uniones también se revela mediante la reflexión. Por ejemplo

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

Produce

external view:
R

internal view:
{ X = [1; 2; 3]
  Y = ["one"; "two"; "three"] }

Valores grandes, cíclicos o profundamente anidados

Los valores estructurados de gran tamaño tienen el formato de un número máximo de nodos de objetos generales de 10000. Los valores profundamente anidados tienen un formato de profundidad de 100. En ambos casos ... se usa para evitar parte de la salida. Por ejemplo

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)

genera una salida grande con algunas partes elided:

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

Los ciclos se detectan en los gráficos de objetos y ... se usan en lugares donde se detectan ciclos. Por ejemplo

type R = { mutable Links: R list }
let r = { Links = [] }
r.Links <- [r]
printfn "%A" r

Produce

{ Links = [...] }

Valores diferidos, null y de función

Los valores diferidos se imprimen como Value is not created texto equivalente o cuando el valor aún no se ha evaluado.

Los valores NULL se imprimen como null a menos que el tipo estático del valor se determine como un tipo de unión donde null es una representación permitida.

Los valores de función de F# se imprimen como su nombre de cierre generado internamente, por ejemplo, <fun:it@43-7>.

Personalización del formato de texto sin formato con StructuredFormatDisplay

Cuando se usa el %A especificador, se respeta la presencia del StructuredFormatDisplay atributo en las declaraciones de tipo. Se puede usar para especificar texto suplente y propiedad para mostrar un valor. Por ejemplo:

[<StructuredFormatDisplay("Counts({Clicks})")>]
type Counts = { Clicks:int list}

printfn "%20A" {Clicks=[0..20]}

Produce

Counts([0; 1; 2; 3;
        4; 5; 6; 7;
        8; 9; 10; 11;
        12; 13; 14;
        15; 16; 17;
        18; 19; 20])

Personalización del formato de texto sin formato invalidando ToString

La implementación predeterminada de ToString es observable en la programación de F#. A menudo, los resultados predeterminados no son adecuados para su uso en la presentación de información orientada al programador o en la salida del usuario, y como resultado es habitual invalidar la implementación predeterminada.

De forma predeterminada, los tipos de registro y unión de F# invalidan la implementación de ToString con una implementación que usa sprintf "%+A". Por ejemplo

type Counts = { Clicks:int list }

printfn "%s" ({Clicks=[0..10]}.ToString())

Produce

{ Clicks = [0; 1; 2; 3; 4; 5; 6; 7; 8; 9; 10] }

Para los tipos de clase, no se proporciona ninguna implementación predeterminada de y se usa el valor predeterminado de ToString .NET, que informa del nombre del tipo. Por ejemplo

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())

Produce

Default structured print gives this:
[MyClassType; MyClassType]
Default ToString gives:
[MyClassType; MyClassType]

Agregar una invalidación para ToString puede dar un mejor formato.

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())

Produce

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])]

Personalización del formato de texto sin formato con StructuredFormatDisplay y ToString

Para lograr un formato coherente para %A los especificadores de formato y %O , combine el uso de StructuredFormatDisplay con una invalidación de ToString. Por ejemplo

[<StructuredFormatDisplay("{DisplayText}")>]
type MyRecord =
    {
        a: int
    }
    member this.DisplayText = this.ToString()

    override _.ToString() = "Custom ToString"

Evaluación de las definiciones siguientes

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]}"

proporciona el texto

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]"

El uso de con la propiedad auxiliar DisplayText significa el hecho de StructuredFormatDisplay que myRec se omite un tipo de registro estructural durante la impresión estructurada y la invalidación de ToString() se prefiere en todas las circunstancias.

Se puede agregar una implementación de la System.IFormattable interfaz para una mayor personalización en presencia de especificaciones de formato .NET.

Impresión estructurada de F# interactivo

F# Interactive (dotnet fsi) usa una versión extendida de formato de texto sin formato estructurado para informar de los valores y permite una personalización adicional. Para obtener más información, vea F# Interactive.

Personalizar las pantallas de depuración

Los depuradores de .NET respetan el uso de atributos como DebuggerDisplay y DebuggerTypeProxy, y afectan a la visualización estructurada de objetos en ventanas de inspección del depurador. El compilador de F# generó automáticamente estos atributos para tipos de unión y registro discriminados, pero no tipos de clase, interfaz o estructura.

Estos atributos se omiten en formato de texto sin formato de F#, pero puede resultar útil implementar estos métodos para mejorar las pantallas al depurar tipos de F#.

Consulte también