Форматирование обычного текста

F# поддерживает проверка форматирование обычного текста с помощью printf, printfnsprintfи связанных функций. Например,

dotnet fsi

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

предоставляет выходные данные

Hello world, 2 + 2 is 4

F# также позволяет форматировать структурированные значения как обычный текст. Например, рассмотрим следующий пример, который форматирует выходные данные как матрицу, например отображение кортежей.

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

Структурированное форматирование обычного текста активируется при использовании %A формата в printf строках форматирования. Он также активируется при форматировании выходных данных значений в интерактивном режиме F#, где выходные данные содержат дополнительные сведения и дополнительно настраиваются. Форматирование обычного текста также можно наблюдать с помощью вызовов x.ToString() объединения F# и значений записей, включая те, которые происходят неявно при отладке, ведении журнала и других инструментах.

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] того, где тип интерпретируется следующим образом:

Спецификатор формата Типы Замечания
%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 является одной десятичной цифрой, dd — одна или несколько десятичных цифр, 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), sbyte (System.SByte),System.Int16int16 (), (),System.UInt16uint16 (),System.Int32uint32int32System.UInt32System.UInt64System.IntPtruint64System.Int64nativeintint64 ()unativeintSystem.UIntPtr Базовые типы с плавающей запятой: float (System.Double), float32 (System.Single) и decimal (System.Decimal).

Необязательная ширина — целое число, указывающее минимальную ширину результата. Например, %6d выводит целое число, префиксируя его пробелами, чтобы заполнить по крайней мере шесть символов. Если ширина равна *, то для указания соответствующей ширины принимается дополнительный целочисленный аргумент.

Допустимые флаги:

Флаг Действие
0 Добавление нулей вместо пробелов для создания требуемой ширины
- Слева оправдывает результат в пределах указанной ширины.
+ Добавьте символ, + если число является положительным - (чтобы соответствовать знаку отрицательных)
символ пробела Добавьте дополнительное пространство, если число положительно (для сопоставления знака "-" для отрицательных)

Флаг printf # недопустим, и ошибка во время компиляции будет сообщаться, если он используется.

Значения форматируются с помощью инвариантного языка и региональных параметров. Параметры языка и региональных параметров не относятся к printf форматированию, за исключением случаев, когда они влияют на результаты %O и %A форматирование. Дополнительные сведения см . в разделе структурированного форматирования обычного текста.

%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 описателя строки форматируются с помощью кавычек. Коды escape-кода не добавляются и вместо этого выводятся необработанные символы. Например,

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

Производит

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

Значения .NET

При форматировании обычного текста с помощью %A описателя объекты .NET, отличные от F#, форматируются с помощью x.ToString() параметров по умолчанию .NET, заданных System.Globalization.CultureInfo.CurrentCulture и System.Globalization.CultureInfo.CurrentUICulture. Например,

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

Для значений последовательности (IEnumerable) используется ограничение глубины 4.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 в объявлениях типов. Это можно использовать для указания суррогатного текста и свойства для отображения значения. Например:

[<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

Чтобы обеспечить согласованное форматирование и %A%O описатели формата, объедините использование 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 со вспомогательным DisplayText свойством означает, что myRec тип структурной записи игнорируется во время структурированной печати, и переопределение ToString() предпочтительнее во всех обстоятельствах.

Реализацию System.IFormattable интерфейса можно добавить для дальнейшей настройки в присутствии спецификаций формата .NET.

Интерактивная структурированная печать F#

В интерактивном режиме F# (dotnet fsi) используется расширенная версия структурированного форматирования обычного текста для отчетов и обеспечивает дополнительную настройку. Дополнительные сведения см. в статье F# Interactive.

Настройка отображения отладки

Отладчики для .NET уважают использование таких атрибутов, как DebuggerDisplay и DebuggerTypeProxy, и они влияют на структурированное отображение объектов в окнах проверки отладчика. Компилятор F# автоматически создал эти атрибуты для различаемых типов объединения и записей, но не для классов, интерфейсов или структур.

Эти атрибуты игнорируются в формате обычного текста F#, но их можно использовать для улучшения отображения при отладке типов F#.

См. также