Строка формата printf относится к параметру управления, используемому классом функции в библиотеках ввода / вывода C и многих других языков программирования. Строка написана на простом языке шаблонов : символы обычно копируются буквально в выходные данные функции, но спецификаторы формата, начинающиеся с символа %
, указывают местоположение и метод для перевода части данных (например, числа) в символы.
«printf» - это имя одной из основных функций вывода C, которое означает «печать с форматированием». Строки формата printf дополняют строки формата scanf, которые обеспечивают форматированный ввод (синтаксический анализ ). В обоих случаях они обеспечивают простую функциональность и фиксированный формат по сравнению с более сложными и гибкими шаблонизаторами или синтаксическими анализаторами, но их достаточно для многих целей.
Многие языки, отличные от C, копируют синтаксис строки формата printf близко или точно в свои собственные функции ввода-вывода.
Несоответствие между спецификаторами формата и типом данных может вызвать сбои и другие уязвимости. Сама форматная строка очень часто представляет собой строковый литерал , который позволяет статический анализ вызова функции. Однако это также может быть значение переменной, допускающее динамическое форматирование, а также уязвимость системы безопасности, известная как эксплойт неконтролируемой строки формата.
Ранние языки программирования, такие как Fortran, использовали специальные операторы с совершенно отличным от других вычислений синтаксисом для построения описаний форматирования. В этом примере формат указан в строке 601, и команда WRITE обращается к нему по номеру строки:
WRITE OUTPUT TAPE 6, 601, IA, IB, IC, AREA 601 FORMAT (4H A =, I5,5H B =, I5,5H C =, I5, 8H AREA =, F10.2, 13H SQUARE UNITS)
АЛГОЛ 68 имел больше функционально-подобных API, но все же использовал специальный синтаксис (разделители $
окружают специальный синтаксис форматирования):
printf (($ "Color" g ", number1" 6d, ", number2" 4zd, ", hex" 16r2d, ", float" - d.2d, ", беззнаковое значение" -3d "." l $, "red", 123456, 89, BIN 255, 3.14, 250));
Но использование обычных вызовов функций и типов данных упрощает язык и компилятор, а также позволяет записывать реализацию ввода / вывода на одном языке. Эти преимущества перевешивают недостатки (например, полное отсутствие безопасности типов во многих случаях), и в большинстве новых языков ввод-вывод не является частью синтаксиса.
C printf
берет свое начало в функции BCPL writef
(1966). По сравнению с printf
, *N
является новой строкой, а порядок ширины поля и типа меняются местами в writef
:
WRITEF («ПРОБЛЕМА% I2-QUEENS ИМЕЕТ РЕШЕНИЯ% I5 * N», NUMQUEENS, COUNT)
Вероятно, первым копированием синтаксиса за пределы языка C была команда оболочки Unix printf
, которая впервые появилась в версии 4 как часть переноса на C.
Форматирование выполняется с помощью заполнителей в строке формата. Например, если программа хотела распечатать возраст человека, она могла бы представить результат, поставив перед ним префикс «Ваш возраст» и используя знак десятичного описателя со знаком d
, чтобы обозначить, что нам нужно целое число. чтобы возраст отображался сразу после этого сообщения, мы можем использовать строку формата:
printf ("Ваш возраст% d", возраст);
Синтаксис для заполнителя формата:
% [параметр] [флаги] [ширина] [. Точность] [длина] тип
Это является расширением POSIX, а не в C99. Поле параметра может быть опущено или может иметь следующий вид:
Символ | Описание |
---|---|
n $ | n - номер параметра, отображаемого с использованием этого спецификатора формата, с учетом предоставленных параметров для вывода несколько раз с использованием различных спецификаторов формата или в разном порядке. Если какой-либо отдельный заполнитель указывает параметр, все остальные заполнители ДОЛЖНЫ также указывать параметр.. Например, printf ("% 2 $ d% 2 $ # x;% 1 $ d% 1 $ # x ", 16,17) производит 17 0x11; 16 0x10 . |
Эта функция в основном используется в локализации, где порядок появления параметров зависит от языка.
В Microsoft Windows, отличной от POSIX, поддержка этой функции помещена в отдельную функцию printf_p.
Поле флагов может содержать ноль или более (в любом порядке):
Символ | Описание |
---|---|
-. (минус) | Выровняйте по левому краю вывод этого заполнителя. (По умолчанию вывод выравнивается по правому краю.) |
+. (плюс) | Добавляет плюс для положительных числовых типов со знаком. положительное = +, отрицательное = -.. (по умолчанию ничего не добавляется перед положительными числами.) |
. (пробел) | Предварительно добавляет пробел для положительного знака -числовые типы. положительный = , отрицательный = -. Этот флаг игнорируется, если существует флаг +.. (По умолчанию ничего не добавляется перед положительными числами.) |
0. (ноль) | Если установлен параметр «ширина» задано, добавляет нули к числовым типам. (По умолчанию пробелы предшествуют.). Например, printf ("% 4X", 3) производит 3 , а printf ("% 04X", 3) производит 0003 . |
'. (апостроф) | Целое число или показатель степени десятичной дроби имеет примененный разделитель группировок тысяч. |
#. (хэш) | Альтернативная форма:. Для типов g и G конечные нули не удаляются.. Для f, F, e, E, g, G, выходные данные всегда содержат десятичную точку.. Для типов o, x, X Текст 0, 0x, 0X, соответственно, добавляется к ненулевым числам. |
Поле ширины определяет минимальное количество символов для вывода и обычно используется для заполнения полей фиксированной ширины в табличных выводах, где в противном случае поля были бы меньше, хотя это не вызывает усечение негабаритных полей.
Поле ширины может быть опущено, или числовое целочисленное значение, или динамическое значение при передаче в качестве другого аргумента, если оно обозначено звездочкой *. Например, printf ("% * d", 5, 10)
приведет к печати 10
с общей шириной 5 символов.
Хотя и не является частью поля ширины, начальный ноль интерпретируется как флаг заполнения нулями, упомянутый выше, а отрицательное значение обрабатывается как положительное значение вместе с флагом выравнивания по левому краю - также упоминалось выше.
Поле точности обычно определяет максимальный предел вывода, в зависимости от конкретного типа форматирования. Для числовых типов с плавающей запятой он определяет количество цифр справа от десятичной запятой, для которых выходные данные должны быть округлены. Для строкового типа он ограничивает количество выводимых символов, после чего строка усекается.
Поле точности может быть опущено, числовое целочисленное значение или динамическое значение при передаче в качестве другого аргумента, если оно обозначено звездочкой *. Например, printf ("%. * S", 3, "abcdef")
приведет к печати abc
.
Поле длины может быть опущено или быть любым из:
Символ | Описание |
---|---|
hh | Для целочисленных типов заставляет printf ожидать целочисленный аргумент размера int, который был повышен с char. |
h | Для целочисленных типов заставляет printf ожидать целочисленный аргумент размером int, который был повышен из короткого. |
l | Для целочисленных типов заставляет printf ожидать аргумент целого числа большого размера. Для типов с плавающей запятой это игнорируется. аргументы с плавающей запятой всегда повышаются до double при использовании в вызове varargs. |
ll | Для целочисленных типов заставляет printf ожидать аргумент длинного целого числа большого размера. |
L | Для типов с плавающей запятой заставляет printf ожидать длинный двойной аргумент. |
z | Для целочисленных типов заставляет printf ожидать целочисленный аргумент размера size_t. |
j | Для целочисленных типов заставляет printf ожидать целочисленный аргумент размером intmax_t. |
t | Для целочисленных типов заставляет printf ожидать целочисленный аргумент размером ptrdiff_t. |
Кроме того, до широкого использования расширений ISO C99 появилось несколько вариантов длины для конкретной платформы:
Символы | Описание |
---|---|
I | Для целочисленных типов со знаком заставляет printf ожидать целочисленный аргумент размера ptrdiff_t; для беззнаковых целочисленных типов заставляет printf ожидать целочисленный аргумент size_t-size. Обычно встречается на платформах Win32 / Win64. |
I32 | Для целочисленных типов заставляет printf ожидать 32-битный (двойное слово) целочисленный аргумент. Обычно встречается на платформах Win32 / Win64. |
I64 | Для целочисленных типов заставляет printf ожидать 64-битный (четверное слово) целочисленный аргумент. Обычно встречается на платформах Win32 / Win64. |
q | Для целочисленных типов заставляет printf ожидать 64-битный (четверное слово) целочисленный аргумент. Обычно встречается на платформах BSD. |
ISO C99 включает файл заголовка inttypes.h
, который включает ряд макросов для использования в платформенно-независимом кодировании printf
. Они должны быть вне двойных кавычек, например printf ("%" PRId64 "\ n", t);
Примеры макросов включают:
Макрос | Описание |
---|---|
PRId32 | Обычно эквивалент I32d (Win32 / Win64) или d |
PRId64 | Обычно эквивалент I64d (Win32 / Win64), lld (32-битные платформы) или ld (64-битные платформ) |
PRIi32 | Обычно эквивалент I32i (Win32 / Win64) или i |
PRIi64 | Обычно эквивалент I64i (Win32 / Win64), lli (32-разрядные платформы) или li (64-разрядные платформы) |
PRIu32 | Обычно эквивалентны I32u (Win32 / Win64) или u |
PRIu64 | Обычно соответствует I64u (Win32 / Win64), llu (32-разрядные платформы) или lu (64-разрядные платформы) |
PRIx32 | Обычно эквивалентно I32x (Win32 / Win64) или x |
PRIx64 | Обычно эквивалент I64x (Win32 / Win64), llx (32-битные платформы) или lx (64- битовые платформы) |
Поле типа может быть любым из:
Символ | Описание |
---|---|
% | Печатает литерал Символ% (этот тип не принимает поля флагов, ширины, точности, длины). |
d, i | int как целое число со знаком. % d и % i являются синонимами вывода, но при использовании с scanf для ввода (где использование % i интерпретирует число как шестнадцатеричный, если ему предшествует 0x, и восьмеричный, если ему предшествует 0.) |
u | Вывести десятичное число unsigned int. |
f, F | double в нормальном (фиксированном ) обозначении. f и F отличаются только тем, как печатаются строки для бесконечного числа или NaN (inf, infinity и nan для f; INF, БЕСКОНЕЧНОСТЬ и NAN для F). |
e, E | двойное значение в стандартной форме (d.ddde ± dd). Преобразование E использует букву E (вместо e) для представления показателя степени. Показатель степени всегда содержит как минимум две цифры; если значение равно нулю, показатель степени равен 00. В Windows показатель степени по умолчанию состоит из трех цифр, например 1.5e002, но это можно изменить с помощью специфичной для Microsoft функции _set_output_format . |
g, G | double в нормальном или экспоненциальном формате, в зависимости от того, что больше подходит для его величины. g использует буквы нижнего регистра, G использует буквы верхнего регистра. Этот тип немного отличается от записи с фиксированной точкой тем, что незначащие нули справа от десятичной точки не включаются. Кроме того, десятичная точка не включается в целые числа. |
x, X | unsigned int как шестнадцатеричное число. x использует строчные буквы, а X использует верхний регистр. |
o | целое число без знака в восьмеричном формате. |
s | строка с завершающим нулем. |
c | char (символ). |
p | void * (указатель на void) в формате, определяемом реализацией. |
a, A | double в шестнадцатеричной системе счисления, начиная с 0x или 0X. a использует буквы нижнего регистра, A использует буквы верхнего регистра. (В iostreams C ++ 11 есть hexfloat, который работает так же). |
n | Ничего не печатает, но записывает количество символов, успешно записанных на данный момент, в параметр целочисленного указателя.. Java: указывает на нейтральную платформу новую строку / возврат каретки.. Примечание: это можно использовать в Неконтролируемый строка формата эксплойты. |
Существует несколько реализаций функций, подобных printf
, которые позволяют расширять мини-язык на основе escape-символов ., что позволяет программисту иметь специальную функцию форматирования для не встроенных типов. Одна из наиболее известных - это (теперь устарелая) glibc register_printf_function()
. Однако он редко используется из-за того, что конфликтует с проверкой строки статического формата. Другой - пользовательский форматировщик Vstr, который позволяет добавлять имена многосимвольных форматов.
Некоторые приложения (например, Apache HTTP Server ) включают свою собственную функцию, подобную printf
, и встраивают в нее расширения. Однако все они, как правило, имеют те же проблемы, что и register_printf_function ()
.
Функция ядра Linux printk
поддерживает несколько способов отображения структур ядра с использованием общей спецификации % p
путем добавления дополнительных символов формата. Например, % pI4
печатает адрес IPv4 в десятичной форме с точками. Это позволяет проверять строку статического формата (части % p
) за счет полной совместимости с обычным printf.
Большинство языков, отличных от C, которые имеют функцию, подобную printf
, обходят недостаток этой функции, просто используя формат % s
и преобразовывая объект в строковое представление. C ++ предлагает заметное исключение, поскольку он имеет функцию printf
, унаследованную от его истории C, но также имеет совершенно другой механизм, который является предпочтительным.
Если синтаксис спецификации преобразования недопустим, поведение не определено и может вызвать завершение программы. Если предоставлено слишком мало аргументов функции для предоставления значений для всех спецификаций преобразования в строке шаблона, или если аргументы не правильного типа, результаты также не определены. Лишние аргументы игнорируются. В ряде случаев неопределенное поведение приводило к "атаке форматной строки " безопасности уязвимостям.
Некоторые компиляторы, такие как Коллекция компиляторов GNU, будут статически проверять форматировать строки printf-подобных функций и предупреждать о проблемах (при использовании флагов -Wall
или -Wformat
). GCC также будет предупреждать об определяемых пользователем функциях в стиле printf, если к функции применяется нестандартный «формат» __attribute__
.
Использование только ширины поля для обеспечения табуляции, как и в формате, подобном % 8d% 8d% 8d
для трех целых чисел в три 8-символьных столбца не гарантируют, что разделение полей будет сохранено, если в данных встречаются большие числа. Отсутствие разделения полей может легко привести к повреждению вывода. В системах, которые поощряют использование программ в качестве строительных блоков в сценариях, такие поврежденные данные часто могут быть перенаправлены и повредят дальнейшую обработку, независимо от того, ожидал ли исходный программист, что результат будет прочитан только человеческими глазами. Такие проблемы можно устранить, включив явные разделители, даже пробелы, во все табличные форматы вывода. Простое изменение опасного примера с предыдущего на % 7d% 7d% 7d
решает эту проблему, идентифицируя форматирование до тех пор, пока числа не станут больше, но затем явно препятствуя их объединению на выходе из-за явно включенных пробелов. Аналогичные стратегии применимы к строковым данным.
Языки, использующие строки формата, которые отличаются от стиля, описанного в этой статье (например, AMPL и Elixir ), языки которые наследуют свою реализацию от JVM или другой среды (например, Clojure и Scala ), и языки, которые не имеют стандартной собственной реализации printf, но имеют внешние библиотеки, которые имитируют поведение printf (например, JavaScript ), не включены в этот список.
printf
, sprintf
и fmt
)print ()
и FileStream.printf ()
)printf
, иногда встроенная в оболочку, как некоторые реализации KornShell (ksh), Bourne again shell ( bash) или Z shell (zsh). Эти команды обычно интерпретируют C экранирования в строке формата.iostream
printf
(Unix) printk
(печать сообщений ядра)scanf
std :: fprintf
printf
: форматированный вывод для печати - Справочник по системным интерфейсам, Единая спецификация UNIX, выпуск 7 из Открытая группа Formatter
спецификация в Java 1.5printf(1)
builtin