Иногда полезно позволить пользователям и программам Lisp контролировать, как генерируется определенный текст, с помощью пользовательских строк управления форматированием. Например, строка форматирования может контролировать, как отображать имя, фамилию и адрес электронной почты кого-либо. Используя функцию
format, описанную в предыдущем разделе, строка форматирования может выглядеть примерно так: "%s %s <%s>". Однако этот подход быстро становится непрактичным, поскольку может быть неясно, какой спецификатор соответствует какой информации.
Более удобная строка форматирования для таких случаев может выглядеть примерно так: "%f %l <%e>", где каждый спецификатор несет больше семантической информации и может быть легко переупорядочен относительно других спецификаторов, что делает такие строки форматирования более настраиваемыми пользователем.
Функция format-spec, описанная в этом разделе, выполняет аналогичную функцию функции format, за исключением того, что она работает со строками управления форматированием, использующими произвольные спецификаторы.
Эта функция возвращает строку, полученную из строки форматирования
template в соответствии с преобразованиями, указанными в spec-alist,
который является alist (см. Списки ассоциаций) вида
(буква . замена). Каждый спецификатор
%буква в template будет заменен на
замена при форматировании результирующей строки.
Символы в template, отличные от спецификаторов форматирования, копируются напрямую в вывод, включая их текстовые свойства, если таковые имеются. Любые текстовые свойства спецификаторов форматирования копируются в их замены.
Использование alist для указания преобразований дает ряд полезных свойств:
ЗАМЕНА также может быть функцией, не принимающей аргументов и возвращающей строку, которая будет использоваться для замены. Она будет вызвана только тогда, когда соответствующая БУКВА используется в ШАБЛОНЕ. Это полезно, например, чтобы избежать запроса ввода, если он не нужен.
Необязательный аргумент ignore-missing указывает, как обрабатывать
спецификаторы в template, которые не найдены в
spec-alist. Если он равен nil или опущен, функция
сигнализирует об ошибке; если он равен ignore, эти спецификаторы
форматирования остаются без изменений в выводе, включая их текстовые свойства, если
таковы; если он равен delete, эти спецификаторы форматирования
удаляются из вывода; любое другое не-nil значение обрабатывается как
ignore, но любые вхождения ‘%%’ также остаются
без изменений в выводе.
Если необязательный аргумент split не равен nil,
вместо возврата одной строки format-spec разделит результат
на список строк на основе мест выполнения подстановок. Например:
(format-spec "foo %b bar" '((?b . "zot")) nil t)
⇒ ("foo " "zot" " bar")
Синтаксис спецификаторов форматирования, принимаемых format-spec,
похож, но не идентичен, тому, который принимается format. В обоих
случаях спецификатор форматирования — это последовательность символов, начинающаяся
с ‘%’ и заканчивающаяся буквенным символом, таким как
‘s’.
В отличие от format, который присваивает конкретные значения
фиксированному набору спецификаторов, format-spec принимает произвольные
спецификаторы и рассматривает их все одинаково. Например:
(setq my-site-info
(list (cons ?s system-name)
(cons ?t (symbol-name system-type))
(cons ?c system-configuration)
(cons ?v emacs-version)
(cons ?e invocation-name)
(cons ?p (number-to-string (emacs-pid)))
(cons ?a user-mail-address)
(cons ?n user-full-name)))
(format-spec "%e %v (%c)" my-site-info)
⇒ "emacs 27.1 (x86_64-pc-linux-gnu)"
(format-spec "%n <%a>" my-site-info)
⇒ "Emacs Developers <emacs-devel@gnu.org>"
Спецификатор форматирования может включать любое количество следующих флаговых символов сразу после ‘%’ для изменения аспектов подстановки.
Этот флаг заставляет любое дополнение, указанное шириной, состоять из символов ‘0’ вместо пробелов.
Этот флаг заставляет любое дополнение, указанное шириной, вставляться справа, а не слева.
Этот флаг заставляет подстановку обрезаться слева до заданной ширины и точности, если они указаны.
Этот флаг заставляет подстановку обрезаться справа до заданной ширины и точности, если они указаны.
Этот флаг преобразует подставляемый текст в верхний регистр (см. Преобразование регистра в Lisp).
Этот флаг преобразует подставляемый текст в нижний регистр (см. Преобразование регистра в Lisp).
Результат использования противоречивых флагов (например, верхнего и нижнего регистра) не определен.
Как и в случае с format, спецификатор форматирования может
включать ширину, которая является десятичным числом, появляющимся после любых
флагов, и точность, которая является десятичной точкой ‘.’
с последующим десятичным числом, появляющимся после любых флагов и ширины.
Если подстановка содержит меньше символов, чем указана ширина, она дополняется слева:
(format-spec "%8a is padded on the left with spaces"
'((?a . "alpha")))
⇒ " alpha is padded on the left with spaces"
Если подстановка содержит больше символов, чем указана точность, она обрезается справа:
(format-spec "%.2a is truncated on the right"
'((?a . "alpha")))
⇒ "al is truncated on the right"
Вот более сложный пример, который сочетает несколько вышеупомянутых функций:
(setq my-battery-info
(list (cons ?p "73") ; Процент
(cons ?L "Battery") ; Статус
(cons ?t "2:23") ; Оставшееся время
(cons ?c "24330") ; Емкость
(cons ?r "10.6"))) ; Скорость разряда
(format-spec "%>^-3L : %3p%% (%05t left)" my-battery-info)
⇒ "BAT : 73% (02:23 left)"
(format-spec "%>^-3L : %3p%% (%05t left)"
(cons (cons ?L "AC")
my-battery-info))
⇒ "AC : 73% (02:23 left)"
Как показывают примеры в этом разделе, format-spec часто
используется для выборочного форматирования набора различных
информационных фрагментов. Это полезно в программах, которые предоставляют
пользовательские строки форматирования, поскольку пользователь может выбрать
форматирование с помощью регулярного синтаксиса и в любом желаемом порядке
только подмножество информации, доступной программе.