Строка формата printf относится к параметру управления, используемому классом функций в библиотеках ввода / вывода C и многих других языков программирования . Строка написана на простом языке шаблонов : символы обычно буквально копируются в выходные данные функции, но спецификаторы формата , начинающиеся с %
символа, указывают местоположение и метод перевода части данных (например, числа) в символы.
«printf» - это имя одной из основных функций вывода языка C, которое означает « print f ormatted». Строки формата printf дополняют строки формата scanf , которые обеспечивают форматированный ввод ( синтаксический анализ ). В обоих случаях они обеспечивают простую функциональность и фиксированный формат по сравнению с более сложными и гибкими шаблонизаторами или синтаксическими анализаторами, но их достаточно для многих целей.
Многие языки, отличные от C, копируют синтаксис строки формата printf точно или точно в свои собственные функции ввода-вывода.
Несоответствие между спецификаторами формата и типом данных может вызвать сбои и другие уязвимости. Сама строка формата очень часто является строковым литералом , который позволяет статический анализ вызова функции. Однако это также может быть значение переменной, допускающее динамическое форматирование, а также уязвимость системы безопасности, известная как эксплойт неконтролируемой строки формата .
История
Ранние языки программирования, такие как Fortran, использовали специальные операторы с совершенно отличным от других вычислений синтаксисом для построения описаний форматирования. В этом примере формат указан в строке 601, а команда WRITE обращается к нему по номеру строки:
ЗАПИСЬ ВЫХОД TAPE 6 , 601 , И.А. , IB , IC , ПЛОЩАДЬ 601 ФОРМАТ ( 4 Н = , I5 , 5 Н В = , I5 , 5 Н С = , I5 , и 8 Н ОБЛАСТЬ = , F10 . 2 , 13 Н КВАДРАТНЫЕ ЕДИНИЦЫ )
Алгол 68 имел более функциональный API , но по-прежнему использовал специальный синтаксис ( $
разделители окружают специальный синтаксис форматирования):
printf (( $ "Color" g ", number1" 6 d , ", number2" 4 zd , ", hex" 16 r2d , ", float" - d .2 d , ", значение без знака" -3 d "." l $ , "красный" , 123456 , 89 , БИН 255 , 3.14 , 250 ));
Но использование обычных вызовов функций и типов данных упрощает язык и компилятор, а также позволяет писать на одном языке реализацию ввода / вывода. Эти преимущества перевешивают недостатки (например, во многих случаях полное отсутствие безопасности типов), и в большинстве новых языков ввод-вывод не является частью синтаксиса.
Кассиопеяне printf
имеет свои истоки в BCPL «S writef
функции (1966). По сравнению с C
и printf
, *N
это escape-последовательность языка BCPL , представляющая символ новой строки (для которого C использует escape-последовательность \n
), а порядок ширины и типа поля спецификации формата меняется на противоположный writef
: [1]
WRITEF ("ПРОБЛЕМА% I2-QUEENS ИМЕЕТ% I5 РЕШЕНИЙ * N"; NUMQUEENS, COUNT)
Вероятно, первым копированием синтаксиса за пределами языка C была команда printf
оболочки Unix , которая впервые появилась в версии 4 как часть переноса на C. [2]
Спецификация заполнителя формата
Форматирование происходит через заполнители в строке формата. Например, если программа хотела распечатать возраст человека, она могла бы представить результат, поставив перед ним префикс «Ваш возраст» и используя знаковый десятичный спецификатор, 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.
Поле флагов
В поле Flags может быть ноль или более (в любом порядке):
Персонаж Описание -
(минус)Выровняйте по левому краю вывод этого заполнителя. (По умолчанию вывод выравнивается по правому краю.) +
(плюс)Добавляет плюс для положительных числовых типов со знаком. положительный = + , отрицательный = - .
(По умолчанию перед положительными числами ничего не добавляется.)
(космос)Предварительно ставит пробел для положительных числовых типов со знаком. положительный =
(По умолчанию перед положительными числами ничего не добавляется.)0
(ноль)Когда указана опция «ширина», перед числовыми типами добавляются нули. (По умолчанию пробелы добавляются к началу.)
Например,printf("%4X",3)
производит3
, аprintf("%04X",3)
производит0003
.'
(апостроф)К целому числу или показателю десятичной дроби применяется разделитель группировок тысяч. #
(хэш)Альтернативная форма:
Для г и Типы G , нули в конце не удаляются.
Для е , F , е , E , г , G , вывод всегда содержит десятичную точку.
Для о , х , X типы, текст 0 , 0x , 0X , соответственно, добавляется к ненулевым числам.
Поле ширины
Поле «Ширина» определяет минимальное количество символов для вывода и обычно используется для заполнения полей фиксированной ширины в табличном выводе, где в противном случае поля были бы меньше, хотя это не приводит к усечению слишком больших полей.
Поле ширины может быть опущено, числовое целочисленное значение или динамическое значение при передаче в качестве другого аргумента, если оно обозначено звездочкой. * . [3] Например, в printf("%*d", 5, 10)
результате 10
будет напечатана общая ширина 5 символов.
Хотя это и не является частью поля ширины, начальный ноль интерпретируется как флаг заполнения нулями, упомянутый выше, а отрицательное значение обрабатывается как положительное значение в сочетании с выравниванием по левому краю. - флаг, также упомянутый выше.
Поле точности
В поле «Точность» обычно указывается максимальный предел вывода, зависящий от конкретного типа форматирования. Для числовых типов с плавающей запятой он определяет количество цифр справа от десятичной запятой, для которых выходные данные должны быть округлены. Для строкового типа он ограничивает количество выводимых символов, после чего строка усекается.
Поле точности может быть опущено, числовое целочисленное значение или динамическое значение при передаче в качестве другого аргумента, если оно обозначено звездочкой. * . Например, printf("%.*s", 3, "abcdef")
приведет abc
к печати.
Поле длины
Поле длины может быть опущено или иметь одно из следующих значений:
Персонаж Описание чч Для целочисленных типов причины printf ожидать int -размерный целочисленный аргумент, который был повышен из char . час Для целочисленных типов причины printf ожидать int -размерный целочисленный аргумент, который был повышен из коротко . л Для целочисленных типов причины printf ожидать целочисленный аргумент длинного размера. Для типов с плавающей запятой это игнорируется. аргументы с плавающей запятой всегда повышаются до double при использовании в вызове varargs. [4]
ll Для целочисленных типов причины printf ожидать длинный целочисленный аргумент длинного размера. L Для типов с плавающей запятой причины printf ожидать длинный двойной аргумент. z Для целочисленных типов причины printf ожидать size_t - целочисленный аргумент размера. j Для целочисленных типов причины printf ожидать intmax_t - целочисленный аргумент. т Для целочисленных типов причины printf ожидать ptrdiff_t -размерный целочисленный аргумент.
Кроме того, до широкого использования расширений ISO C99 появилось несколько вариантов длины для конкретных платформ:
Символы Описание я Для целочисленных типов со знаком причины printf ожидать ptrdiff_t - целочисленный аргумент размера; для беззнаковых целочисленных типов причины printf ожидать size_t - целочисленный аргумент размера. Обычно встречается на платформах 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 ) или я PRIi64 Обычно эквивалентно I64i ( Win32 / Win64 ), lli ( 32-битные платформы ) или li ( 64-битные платформы ) PRIu32 Обычно эквивалентно I32u ( Win32 / Win64 ) или ты PRIu64 Обычно эквивалентно I64u ( Win32 / Win64 ), llu ( 32-битные платформы ) или lu ( 64-битные платформы ) PRIx32 Обычно эквивалентно I32x ( Win32 / Win64 ) или Икс PRIx64 Обычно эквивалентно I64x ( Win32 / Win64 ), llx ( 32-битные платформы ) или lx ( 64-битные платформы )
Поле типа
Поле Тип может быть любым из:
Персонаж Описание % Печатает буквальный % символ (этот тип не принимает никаких полей флагов, ширины, точности, длины). d , я int как целое число со знаком . % d и % i являются синонимами вывода, но отличаются при использовании с scanf
для ввода (где используется % i интерпретирует число как шестнадцатеричное, если ему предшествует 0x и восьмеричное, если ему предшествует 0. )ты Печать десятичной дроби беззнаковое целое . е , F double в нормальной записи (с фиксированной точкой ). е и F отличается только тем, как печатаются строки для бесконечного числа или NaN ( инф , бесконечность и нан для f ; INF , БЕСКОНЕЧНОСТЬ и NAN для F ). е , E двойное значение в стандартной форме ( d . ддд е ± дд ). An В преобразовании E используется буква E (а не д ) ввести показатель степени. Показатель степени всегда состоит как минимум из двух цифр; если значение равно нулю, показатель степени равен 00 . В Windows показатель степени по умолчанию состоит из трех цифр, например 1.5e002 , но это может быть изменено специфической _set_output_format
функцией Microsoft .г , грамм double в нормальном или экспоненциальном представлении, в зависимости от того, что больше подходит для его величины. g использует строчные буквы, G использует заглавные буквы. Этот тип немного отличается от записи с фиксированной точкой тем, что незначащие нули справа от десятичной точки не включаются. Кроме того, десятичная точка не включается в целые числа. х , Икс unsigned int как шестнадцатеричное число. x использует строчные буквы и X использует верхний регистр. о беззнаковое int в восьмеричном формате. s Строка с завершающим нулем . c char (персонаж). п void * (указатель на void) в формате, определяемом реализацией. а , А double в шестнадцатеричной системе счисления, начиная с 0x или 0X . a использует строчные буквы, A использует заглавные буквы. [5] [6] (iostreams C ++ 11 имеют hexfloat, который работает так же). п Ничего не печатает, но записывает количество уже записанных символов в параметр целочисленного указателя.
В Java это печатает новую строку. [7]
Заполнители нестандартного формата
Существует несколько реализаций printf
-подобных функций, которые позволяют расширять мини-язык на основе escape-символов , что позволяет программисту иметь специальную функцию форматирования для не встроенных типов. Одним из наиболее хорошо известных является ( в настоящее время не рекомендуется) Glibc «s . Однако он редко используется из-за того, что конфликтует с проверкой строки статического формата. Другой - настраиваемые форматеры Vstr , которые позволяют добавлять имена многосимвольных форматов.register_printf_function()
Некоторые приложения (например, HTTP-сервер Apache ) включают в себя свои собственные printf
-подобные функции и встраивают в них расширения. Однако у всех них, как правило, есть те же проблемы, что и register_printf_function()
у них.
Функция ядра Linux printk
поддерживает несколько способов отображения структур ядра с использованием общей %p
спецификации путем добавления дополнительных символов формата. [8] Например, %pI4
выводит IPv4- адрес в десятичной форме с точками. Это позволяет проверять строку статического формата ( %p
части) за счет полной совместимости с обычным printf.
Большинство языков, в которых есть printf
-подобная функция, обходят недостаток этой функции, просто используя %s
формат и преобразовывая объект в строковое представление.
Уязвимости
Недействительные спецификации преобразования
Если слишком мало аргументов функции предоставлено для предоставления значений для всех спецификаций преобразования в строке шаблона, или если аргументы не правильного типа, результаты будут неопределенными, что может привести к сбою. Реализации не согласуются с тем, используют ли синтаксические ошибки в строке аргумент и какой тип аргумента они используют. Лишние аргументы игнорируются. В ряде случаев неопределенное поведение приводило к уязвимостям системы безопасности « Атака форматной строки » . В большинстве соглашений о вызовах C или C ++ аргументы могут передаваться в стек, что означает, что в случае слишком малого числа аргументов printf будет читать за пределами текущего стекового фрейма, что позволяет злоумышленнику читать стек.
Некоторые компиляторы, такие как GNU Compiler Collection , будут статически проверять строки формата printf-подобных функций и предупреждать о проблемах (при использовании флагов -Wall
или -Wformat
). GCC также будет предупреждать об определяемых пользователем функциях в стиле printf, если к функции применяется нестандартный «формат» __attribute__
.
Ширина поля по сравнению с явными разделителями в табличном выводе
Использование только ширины полей для табуляции, как в случае формата %8d%8d%8d
трех целых чисел в трех 8-символьных столбцах, не гарантирует, что разделение полей будет сохранено, если в данных встречаются большие числа. Отсутствие разделения полей может легко привести к повреждению вывода. В системах, которые поощряют использование программ в качестве строительных блоков в сценариях, такие поврежденные данные часто могут быть перенаправлены и повредят дальнейшую обработку, независимо от того, ожидал ли исходный программист, что результат будет прочитан только человеческими глазами. Такие проблемы можно устранить, включив явные разделители, даже пробелы, во все табличные форматы вывода. Простое изменение опасного примера с предыдущего на %7d %7d %7d
устранение этой проблемы, идентичное форматирование до тех пор, пока числа не станут больше, но затем явное предотвращение их объединения при выводе из-за явно включенных пробелов. Аналогичные стратегии применимы к строковым данным.
Запись в память
Хотя функция вывода находится на поверхности, она printf
позволяет записывать в ячейку памяти, указанную аргументом через %n
. Эта функция иногда используется как часть более сложных атак на строку формата. [9]
Эта %n
функциональность также делает printf
случайным Тьюринг завершенным даже с хорошо сформированным набором аргументов. Игра в крестики-нолики, написанная в формате строки, стала победителем 27-го IOCCC . [10]
Языки программирования с printf
Языки, которые используют строки формата, которые отличаются от стиля, описанного в этой статье (например, AMPL и Elixir ), языки, которые наследуют свою реализацию от JVM или другой среды (например, Clojure и Scala ), и языки, не имеющие стандартного собственного printf реализация, но есть внешние библиотеки, имитирующие поведение printf (например, JavaScript ), не включены в этот список.
- awk (через sprintf)
- C
- С ++ (также перегружены операторами сдвига и манипуляторы в качестве альтернативы для форматированного вывода - см iostream и iomanip )
- Цель-C
- D
- F #
- G ( LabVIEW )
- GNU MathProg
- GNU Octave
- Идти
- Haskell
- J
- Java (начиная с версии 1.5) и языки JVM
- Джулия (через его стандартную библиотеку Printf; [11] библиотека Formatting.jl добавляет общее форматирование в стиле Python и «часть этого пакета в стиле c направлена на то, чтобы обойти ограничение, заключающееся в том, что @sprintf должен принимать буквальный строковый аргумент»).
- Lua (string.format)
- Клен
- MATLAB
- Макс (через объект sprintf)
- Мифрил
- PARI / GP
- Perl
- PHP
- Python (через
%
оператор) [12] - р
- Рака (через
printf
,sprintf
иfmt
) - Красный / Система
- Рубин
- Tcl (через команду форматирования)
- Transact-SQL (через xp_sprintf )
- Вала (через
print()
иFileStream.printf()
) - printfУтилита командный, иногда встроен в оболочку, например, с некоторыми реализациями KornShell (КШ), Борн снова оболочки (Bash) или Z оболочка (ЗШ). Эти команды обычно интерпретируют escape-символы C в строке формата.
Смотрите также
- Формат (Common Lisp)
- Стандартная библиотека C
- Атака на форматную строку
iostream
- ML (язык программирования)
- printf отладка
- printf (Unix)
printk
(распечатать сообщения ядра)scanf
- строковая интерполяция
Рекомендации
- ^ "BCPL" . cl.cam.ac.uk . Проверено 19 марта 2018 .
- ^ Макилрой, доктор медицины (1987). Читатель Research Unix: аннотированные выдержки из Руководства программиста, 1971–1986 (PDF) (технический отчет). CSTR. Bell Labs. 139.
- ^ «printf - Справочник по C ++» . cplusplus.com . Проверено 10 июня 2020 .
- ^ ISO / IEC (1999). ISO / IEC 9899: 1999 (E): Языки программирования - C §7.19.6.1, параграф 7
- ^ " " Справочное руководство по библиотеке GNU C "," 12.12.3 Таблица преобразований вывода " " . Gnu.org . Проверено 17 марта 2014 года .
- ^ "printf" ( % a добавлен в C99)
- ^ «Форматирование числового вывода на печать» . Учебники по Java . Oracle Inc . Проверено 19 марта 2018 .
- ^ «Документация по ядру Linux / printk-sizes.txt» . Git.kernel.org . Проверено 17 марта 2014 года .
- ^ https://www.exploit-db.com/docs/english/28476-linux-format-string-exploitation.pdf
- ^ https://www.ioccc.org/2020/carlini/index.html
- ^ «Стандартная библиотека Printf» . Руководство по языку Julia . Проверено 22 февраля 2021 года .
- ^ «Встроенные типы: форматирование строк в стиле printf » , Стандартная библиотека Python, Python Software Foundation , получено 24 февраля 2021 г.
Внешние ссылки
- Справочник по C ++ для std::fprintf
- Краткий справочник спецификаций формата gcc printf
- : вывод с форматированием для печати - Справочник по системным интерфейсам, Единая спецификация UNIX , Выпуск 7 от The Open Group
- FormatterСпецификации в Java 1.5
- printf(1)Встроенный GNU Bash