Spec-Zone.ru › Nim

std/strformat

Исходный кодРедактировать

Интерполяция строк / форматирование, вдохновленное f-строками Python.

fmt vs. &

Вы можете использовать либо fmt , либо унарный оператор & для форматирования. Разница между ними тонкая, но важная.

Синтаксис fmt"{expr}" более эстетичен, но скрывает небольшую «ловушку». Строка — это обобщённая строковая литерал без интерпретации спецсимволов. Это имеет некоторые неожиданные последствия:

Пример:

import std/strformat
let msg = "hello"
assert fmt"{msg}\n" == "hello\\n"

Поскольку литерал — это строковая литерал без интерпретации спецсимволов, \n не интерпретируется как последовательность escape-символов.

Существует несколько способов обойти эту проблему, в том числе использование оператора &:

Пример:

import std/strformat
let msg = "hello"

assert &"{msg}\n" == "hello\n"

assert fmt"{msg}{'\n'}" == "hello\n"
assert fmt("{msg}\n") == "hello\n"
assert "{msg}\n".fmt == "hello\n"
Выбор стиля остается за вами.

Форматирование строк

Пример:

import std/strformat
assert &"""{"abc":>4}""" == " abc"
assert &"""{"abc":<4}""" == "abc "

Форматирование чисел с плавающей точкой

Пример:

import std/strformat
assert fmt"{-12345:08}" == "-0012345"
assert fmt"{-1:3}" == " -1"
assert fmt"{-1:03}" == "-01"
assert fmt"{16:#X}" == "0x10"

assert fmt"{123.456}" == "123.456"
assert fmt"{123.456:>9.3f}" == "  123.456"
assert fmt"{123.456:9.3f}" == "  123.456"
assert fmt"{123.456:9.4f}" == " 123.4560"
assert fmt"{123.456:>9.0f}" == "     123."
assert fmt"{123.456:<9.4f}" == "123.4560 "

assert fmt"{123.456:e}" == "1.234560e+02"
assert fmt"{123.456:>13e}" == " 1.234560e+02"
assert fmt"{123.456:13e}" == " 1.234560e+02"

Выражения

Пример:

import std/strformat
let x = 3.14
assert fmt"{(if x!=0: 1.0/x else: 0):.5}" == "0.31847"
assert fmt"""{(block:
    var res: string
    for i in 1..15:
      res.add (if i mod 15 == 0: "FizzBuzz"
        elif i mod 5 == 0: "Buzz"
        elif i mod 3 == 0: "Fizz"
        else: $i) & " "
    res)}""" == "1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz "

Отладка строк

fmt"{expr=}" раскрывается до fmt"expr={expr}", а именно текста выражения, знака равенства и результата вычисления выражения.

Пример:

import std/strformat
assert fmt"{123.456=}" == "123.456=123.456"
assert fmt"{123.456=:>9.3f}" == "123.456=  123.456"

let x = "hello"
assert fmt"{x=}" == "x=hello"
assert fmt"{x =}" == "x =hello"

let y = 3.1415926
assert fmt"{y=:.2f}" == fmt"y={y:.2f}"
assert fmt"{y=}" == fmt"y={y}"
assert fmt"{y = : <8}" == fmt"y = 3.14159 "

proc hello(a: string, b: float): int = 12
assert fmt"{hello(x, y) = }" == "hello(x, y) = 12"
assert fmt"{x.hello(y) = }" == "x.hello(y) = 12"
assert fmt"{hello x, y = }" == "hello x, y = 12"
Обратите внимание на чувствительность к пробелам:

Пример:

import std/strformat
let x = "12"
assert fmt"{x=}" == "x=12"
assert fmt"{x =:}" == "x =12"
assert fmt"{x =}" == "x =12"
assert fmt"{x= :}" == "x= 12"
assert fmt"{x= }" == "x= 12"
assert fmt"{x = :}" == "x = 12"
assert fmt"{x = }" == "x = 12"
assert fmt"{x   =  :}" == "x   =  12"
assert fmt"{x   =  }" == "x   =  12"

Подробности реализации

Выражение, подобное &"{key} is {value:arg} {{z}}", преобразуется в:

var temp = newStringOfCap(educatedCapGuess)
temp.formatValue(key, "")
temp.add(" is ")
temp.formatValue(value, arg)
temp.add(" {z}")
temp

Части строки, заключенные в фигурные скобки, интерпретируются как код Nim. Чтобы экранировать { или }, удвойте их.

Однако внутри фигурного выражения {, }, должны экранироваться обратной косой чертой.

Для включения вычисления выражений Nim в фигурных скобках, двоеточия внутри скобок не нуждаются в экранировании.

Пример:

import std/strformat
let x = "hello"
assert fmt"""{ "\{(" & x & ")\}" }""" == "{(hello)}"
assert fmt"""{{({ x })}}""" == "{(hello)}"
assert fmt"""{ $(\{x:1,"world":2\}) }""" == """[("hello", 1), ("world", 2)]"""

& делегирует большую часть работы открытому перегруженному набору formatValue процедур. Требуемая сигнатура для типа T, поддерживающего форматирование, обычно proc formatValue(result: var string; x: T; specifier: string).

Подвыражение после двоеточия (arg в &"{key} is {value:arg} {{z}}") является необязательным. Оно будет передано в качестве последнего аргумента formatValue . Если двоеточие с подвыражением отсутствует, вместо него будет взята пустая строка.

Для строк и числовых типов необязательный аргумент — это так называемый "стандартный спецификатор формата".

Стандартные спецификаторы формата для строк, целых чисел и чисел с плавающей точкой

Общая форма стандартного спецификатора формата:

[[fill]align][sign][#][0][minimumwidth][.precision][type]

Квадратные скобки [] обозначают необязательный элемент.

Необязательный флаг выравнивания align может быть одним из следующих:

<
Принудительно выравнивает поле слева внутри доступного пространства. (Это значение по умолчанию для строк.)
>
Принудительно выравнивает поле справа внутри доступного пространства. (Это значение по умолчанию для чисел.)
^
Принудительно центрирует поле внутри доступного пространства.

Обратите внимание, что, если не определена минимальная ширина поля, ширина поля всегда будет такой же, как размер данных для заполнения, поэтому опция выравнивания в этом случае не имеет значения.

Необязательный символ fill определяет символ, используемый для заполнения поля до минимальной ширины. Символ заполнения, если он присутствует, должен следовать за флагом выравнивания.

Опция sign действительна только для числовых типов и может быть одной из следующих:

Знак Значение
+ Указывает, что знак должен использоваться как для положительных, так и для отрицательных чисел.
- Указывает, что знак должен использоваться только для отрицательных чисел (это поведение по умолчанию).
(пробел) Указывает, что для положительных чисел должен использоваться ведущий пробел.

Если символ # присутствует, целые числа используют "альтернативную форму" форматирования. Это означает, что двоичное, восьмеричное и шестнадцатеричное представление будут предваряться 0b, 0o и 0x соответственно.

width — десятичное целое число, определяющее минимальную ширину поля. Если не указано, ширина поля определяется содержимым.

Если перед полем ширины стоит ноль (0 ), это включает нулевое заполнение.

precision — десятичное число, указывающее, сколько цифр должно быть отображено после десятичной точки при преобразовании чисел с плавающей точкой. Для нечисловых типов поле указывает максимальный размер поля — другими словами, сколько символов будет использовано из содержимого поля. Точность игнорируется для преобразований целых чисел.

Наконец, type определяет, как должны быть представлены данные.

Доступные типы представления целых чисел:

Тип Результат
b Двоичное. Выводит число в двоичной системе счисления.
d Десятичное целое число. Выводит число в десятичной системе счисления.
o Восьмеричное представление. Выводит число в восьмеричной системе счисления.
x Шестнадцатеричное представление (маленькие буквы). Выводит число в шестнадцатеричной системе счисления, используя строчные буквы для цифр выше 9.
X Шестнадцатеричное представление (большие буквы). Выводит число в шестнадцатеричной системе счисления, используя заглавные буквы для цифр выше 9.
(Нет) То же самое, что и d .

Доступные типы представления чисел с плавающей точкой:

Тип Результат
e Экспоненциальная запись. Выводит число в экспоненциальной записи, используя букву e для обозначения показателя степени.
E Экспоненциальная запись. То же, что и e , но преобразует число в заглавные буквы.
f Запись с фиксированной точкой. Отображает число как число с фиксированной точкой.
F Запись с фиксированной точкой. То же, что и f , но преобразует число в заглавные буквы.
g Общий формат. Выводит число как число с фиксированной точкой, если число не слишком большое, в противном случае переключается на e экспоненциальную запись.
G Общий формат. То же, что и g , но переключается на E если число становится слишком большим.
i Комплексный общий формат. Поддерживается только для комплексных чисел, которые выводятся в математическом формате (RE+IMj). Вещественная и мнимая части выводятся в общем формате g по умолчанию, но возможно комбинировать этот формат с другими (например, jf).
(Нет) Аналогично g, но выводит по крайней мере одну цифру после десятичной точки.

Ограничения

Из-за чёткого порядка, в котором расширяются шаблоны и макросы, strformat не может расширить аргументы шаблона:

template myTemplate(arg: untyped): untyped =
  echo "arg is: ", arg
  echo &"--- {arg} ---"

let x = "abc"
myTemplate(x)

Сначала расширяется шаблон myTemplate, где каждый идентификатор arg заменяется своим аргументом. arg внутри строки формата не виден в этом процессе, потому что это часть литерала строковой константы. Это пока не идентификатор. Затем макрос strformat создаёт идентификатор arg из литерала строки, идентификатор, который больше не может быть разрешён.

Обходным путём является привязка аргумента шаблона к новой локальной переменной.

template myTemplate(arg: untyped): untyped =
  block:
    let arg1 {.inject.} = arg
    echo "arg is: ", arg1
    echo &"--- {arg1} ---"

Использование {.inject.} здесь снова необходимо из-за порядка расширения шаблонов и гигиеничных шаблонов. Но поскольку мы обычно хотим сохранить гигиеничность myTemplate, и не хотим, чтобы arg1 был внедрён в контекст, где расширяется myTemplate, всё обернуто в block.

Перспективы развития

Фигурное выражение с запятыми, как {x, argA, argB} , может быть преобразовано в formatValue(result, x, argA, argB) , чтобы поддерживать форматировщики, которым не нужно анализировать пользовательский язык внутри пользовательского языка, а вместо этого они предпочитают использовать существующий синтаксис Nim. Это также улучшит читаемость, поскольку в однобуквенных DSL можно уместить ограниченное количество информации.

Импорты

macros, parseutils, unicode, strutils

Типы

StandardFormatSpecifier = object
  fill*, align*: char        ## Desired fill and alignment.
  sign*: char                ## Desired sign.
  alternateForm*: bool       ## Whether to prefix binary, octal and hex numbers
                             ## with `0b`, `0o`, `0x`.
  padWithZero*: bool         ## Whether to pad with zeros rather than spaces.
  minimumWidth*, precision*: int ## Desired minimum width and precision.
  typ*: char                 ## Type like 'f', 'g' or 'd'.
  endPosition*: int          ## End position in the format specifier after
                             ## `parseStandardFormatSpecifier` returned.
Тип, описывающий "стандартные спецификаторы формата". Исходный код Редактировать

Процедуры

proc alignString(s: string; minimumWidth: int; align = '\x00'; fill = ' '): string {.
    ...raises: [], tags: [], forbids: [].}
Выравнивает s используя символ fill. Это актуально только если вы хотите написать пользовательскую format процедуру, которая должна поддерживать стандартные спецификаторы формата. Исходный код Редактировать
proc formatValue(result: var string; value: SomeFloat; specifier: static string)
Стандартная реализация форматирования для SomeFloat. Вызывать её напрямую не имеет смысла, но она необходима для макроса &. Исходный код Редактировать
proc formatValue(result: var string; value: SomeFloat; specifier: string)
Стандартная реализация форматирования для SomeFloat. Вызывать её напрямую не имеет смысла, но она необходима для макроса &. Исходный код Редактировать
proc formatValue(result: var string; value: string; specifier: static string)
Стандартная реализация форматирования для string. Вызывать её напрямую не имеет смысла, но она необходима для макроса &. Исходный код Редактировать
proc formatValue(result: var string; value: string; specifier: string) {.
    ...raises: [ValueError], tags: [], forbids: [].}
Стандартная реализация форматирования для string. Вызывать её напрямую не имеет смысла, но она необходима для макроса &. Исходный код Редактировать
proc formatValue[T: SomeInteger](result: var string; value: T;
                                 specifier: static string)
Стандартная реализация форматирования для SomeInteger. Вызывать её напрямую не имеет смысла, но она необходима для макроса &. Исходный код Редактировать
proc formatValue[T: SomeInteger](result: var string; value: T; specifier: string)
Стандартная реализация форматирования для SomeInteger. Вызывать её напрямую не имеет смысла, но она необходима для макроса &. Исходный код Редактировать
proc parseStandardFormatSpecifier(s: string; start = 0;
                                  ignoreUnknownSuffix = false): StandardFormatSpecifier {.
    ...raises: [ValueError], tags: [], forbids: [].}
Экспортируемая вспомогательная процедура, которая анализирует "стандартные спецификаторы формата", как указано в грамматике:
[[fill]align][sign][#][0][minimumwidth][.precision][type]

Это актуально только если вы хотите написать пользовательскую format процедуру, которая должна поддерживать стандартные спецификаторы формата. Если ignoreUnknownSuffix равно true, то неизвестный суффикс после поля type не является ошибкой.

Исходный код Редактировать

Шаблоны

template `&`(pattern: string{lit}): string {.callsite.}
&pattern эквивалентно pattern.fmt. Для спецификаций макроса &, см. документацию модуля.

Пример:

let x = 7
assert &"{x}\n" == "7\n" # regular string literal
assert &"{x}\n" == "{x}\n".fmt # `fmt` can be used instead
assert &"{x}\n" != fmt"{x}\n" # see `fmt` docs, this would use a raw string literal
Исходный код Редактировать
template fmt(pattern: static string): untyped {.callsite.}
Псевдоним для fmt(pattern, '{', '}'). Исходный код Редактировать
template fmt(pattern: static string; openChar: static char;
             closeChar: static char): string {.callsite.}
Интерполирует pattern используя символы в области видимости.

Пример:

let x = 7
assert "var is {x * 2}".fmt == "var is 14"
assert "var is {{x}}".fmt == "var is {x}" # escape via doubling
const s = "foo: {x}"
assert s.fmt == "foo: 7" # also works with const strings

assert fmt"\n" == r"\n" # raw string literal
assert "\n".fmt == "\n" # regular literal (likewise with `fmt("\n")` or `fmt "\n"`)

Пример:

# custom `openChar`, `closeChar`
let x = 7
assert "<x>".fmt('<', '>') == "7"
assert "<<<x>>>".fmt('<', '>') == "<7>"
assert "`x`".fmt('`', '`') == "7"
Исходный код Редактировать

© 2006–2024 Andreas Rumpf
Licensed under the MIT License.
https://nim-lang.org/docs/strformat.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API