Строка формата printf - printf format string

Пример функции printf.

строка формата printf относится к параметру управления, используемому классом функции в библиотеках ввода / вывода C и многие другие языки программирования. Строка записывается простым язык шаблона: символы обычно копируются буквально в вывод функции, но спецификаторы формата, которые начинаются с % символ, укажите расположение и метод перевода части данных (например, числа) в символы.

printf - это имя одной из основных функций вывода языка Си, обозначающее "печать formatted ». Строки формата. printf дополняют строки формата scanf, которые обеспечивают форматированный ввод (разбор ). В обоих случаях они обеспечивают простую функциональность и фиксированный формат по сравнению с более сложными и гибкими шаблонизаторами или синтаксическими анализаторами, но их достаточно для многих целей.

Многие языки, отличные от C, копируют синтаксис строки формата printf точно или точно в свои собственные функции ввода-вывода.

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

История

Ранние языки программирования, такие как Фортран использовали специальные операторы с совершенно отличным от других вычислений синтаксисом для построения описаний форматирования. В этом примере формат указан в строке 601, а команда WRITE обращается к нему по номеру строки:

 ЗАПИСЫВАТЬ ВЫХОД ЛЕНТА 6, 601, Я, IB, IC, ПЛОЩАДЬ 601  ФОРМАТ (4ЧАС А= ,I5,5ЧАС  B= ,I5,5ЧАС  C= ,I5,&        8ЧАС  ПЛОЩАДЬ= ,F10.2, 13ЧАС КВАДРАТ ЕДИНИЦЫ)

АЛГОЛ 68 был более функциональным API, но по-прежнему использовал специальный синтаксис ( $ разделители окружают специальный синтаксис форматирования):

printf(($"Цвет "грамм", номер 1 "6d,", номер 2 "4zd,"шестнадцатеричный"16r2d,", плавать "-d.2d,", беззнаковое значение"-3d"."l $,         "красный", 123456, 89, BIN 255, 3.14, 250));

Но использование обычных вызовов функций и типов данных упрощает язык и компилятор, а также позволяет записывать реализацию ввода / вывода на одном языке. Эти преимущества перевешивают недостатки (например, полное отсутствие безопасности типов во многих случаях), и в большинстве новых языков ввод-вывод не является частью синтаксиса.

C printf берет свое начало в BCPL с writef функция (1966). В сравнении с C и printf, * N является BCPL язык escape-последовательность, представляющая символ новой строки (для которой C использует escape-последовательность п), а порядок ширины и типа поля спецификации формата меняется на обратный в writef:[1]

WRITEF ("ПРОБЛЕМА% I2-QUEENS ИМЕЕТ% I5 РЕШЕНИЙ * N"; NUMQUEENS, COUNT)

Вероятно, первым копированием синтаксиса за пределами языка C был Unix printf команда оболочки, которая впервые появилась в Версия 4, в составе порта на С.[2]

Спецификация заполнителя формата

Форматирование происходит через заполнители в строке формата. Например, если программа хочет распечатать возраст человека, она может представить результат, поставив перед ним префикс «Ваш возраст» и используя знак десятичного описателя со знаком. d чтобы обозначить, что мы хотим, чтобы целое число для возраста отображалось сразу после этого сообщения, мы можем использовать строку формата:

printf("Ваш возраст% d", возраст);

Синтаксис

Синтаксис для заполнителя формата:

%[параметр][флаги][ширина][.точность][длина]тип

Поле параметра

Это POSIX расширение, а не в C99. Поле параметра может быть опущено или может быть:

ХарактерОписание
п$п - это номер параметра, отображаемого с использованием этого спецификатора формата, что позволяет выводить предоставленные параметры несколько раз с использованием различных спецификаторов формата или в разном порядке. Если какой-либо отдельный заполнитель указывает параметр, все остальные заполнители ДОЛЖНЫ также указывать параметр.
Например, 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.
'
(апостроф)
К целому числу или показателю десятичной дроби применяется разделитель группировок тысяч.
#
(хеш)
Альтернативная форма:
За грамм и грамм типы, нули в конце не удаляются.
За ж, F, е, E, грамм, грамм типов, вывод всегда содержит десятичную точку.
За о, Икс, Икс типы, текст 0, 0x, 0X, соответственно, добавляется к ненулевым числам.

Поле ширины

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

Поле ширины может быть опущено, числовое целочисленное значение или динамическое значение при передаче в качестве другого аргумента, если оно указано звездочкой. *.[3] Например, printf ("% * d", 5, 10) приведет к 10 печатается общей шириной 5 знаков.

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

Поле точности

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

Поле точности может быть опущено, числовое целочисленное значение или динамическое значение при передаче в качестве другого аргумента, если оно обозначено звездочкой. *. Например, printf («%. * s», 3, «abcdef») приведет к abc печатается.

Поле длины

Поле длины может быть опущено или иметь одно из следующих значений:

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

Для типов с плавающей запятой это игнорируется. плавать аргументы всегда продвигаются к двойной при использовании в вызове 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-битные платформы) или же Ли (64-битные платформы)
PRIu32Обычно эквивалентно I32u (Win32 / Win64) или же ты
PRIu64Обычно эквивалентно I64u (Win32 / Win64), llu (32-битные платформы) или же Лу (64-битные платформы)
PRIx32Обычно эквивалентно I32x (Win32 / Win64) или же Икс
PRIx64Обычно эквивалентно I64x (Win32 / Win64), llx (32-битные платформы) или же лк (64-битные платформы)

Поле типа

Поле Тип может быть любым из:

ХарактерОписание
%Печатает буквальный % символ (этот тип не принимает поля флагов, ширины, точности, длины).
d, яint как подписанный целое число. % d и являются синонимами вывода, но отличаются при использовании с сканф для ввода (где используется интерпретирует число как шестнадцатеричное, если ему предшествует 0x, и восьмеричное, если ему предшествует 0.)
тыПечать десятичной дроби беззнаковое целое.
ж, Fдвойной в нормальном (фиксированная точка ) обозначение. ж и F отличается только тем, как печатаются строки для бесконечного числа или NaN (инф, бесконечность и нан за ж; INF, БЕСКОНЕЧНОСТЬ и NAN за F).
е, Eдвойной значение в стандартной форме (d.ддде ±дд). An E преобразование использует букву E (скорее, чем е) ввести показатель степени. Показатель степени всегда состоит как минимум из двух цифр; если значение равно нулю, показатель степени равен 00. В Windows показатель степени по умолчанию состоит из трех цифр, например 1.5e002, но это может быть изменено специфичным для Microsoft _set_output_format функция.
грамм, граммдвойной в нормальном или экспоненциальном представлении, в зависимости от того, что больше подходит для его величины. грамм использует строчные буквы, грамм использует заглавные буквы. Этот тип немного отличается от записи с фиксированной точкой тем, что незначащие нули справа от десятичной точки не включаются. Кроме того, десятичная точка не включается в целые числа.
Икс, Иксбеззнаковое целое как шестнадцатеричный номер. Икс использует строчные буквы и Икс использует верхний регистр.
обеззнаковое целое в восьмеричном.
sстрока с завершающим нулем.
cchar (персонаж).
ппустота* (указатель на void) в формате, определяемом реализацией.
а, Адвойной в шестнадцатеричной системе счисления, начиная с 0x или же 0X. а использует строчные буквы, А использует заглавные буквы.[5][6] (Iostreams C ++ 11 имеют hexfloat это работает так же).
пНичего не печатает, но записывает количество успешно записанных на данный момент символов в параметр целочисленного указателя.
Java: указывает на нейтральную платформу новую строку / возврат каретки.[7]
Примечание: это можно использовать в Строка неконтролируемого формата подвиги.

Заполнители нестандартного формата

Есть несколько реализаций printf-подобные функции, которые позволяют расширять escape-символ -основан мини-язык, таким образом позволяя программисту иметь специальную функцию форматирования для не встроенных типов. Один из самых известных - (теперь не рекомендуется) glibc с register_printf_function (). Однако он редко используется из-за того, что конфликтует с проверкой строки статического формата. Другой Пользовательские форматеры VSTR, что позволяет добавлять имена в многосимвольном формате.

Некоторые приложения (например, HTTP-сервер Apache ) включать свои собственные printf-подобная функция, и встраивать в нее расширения. Однако все они, как правило, имеют те же проблемы, что и register_printf_function () имеет.

В Ядро Linux printk функция поддерживает несколько способов отображения структур ядра с помощью универсального %п спецификация, по добавление дополнительные символы формата.[8] Например, % pI4 печатает IPv4 адрес в десятичной форме с точками. Это позволяет проверять строку статического формата ( %п часть) за счет полной совместимости с обычным printf.

Большинство языков, отличных от C, которые имеют printf-подобная функция позволяет обойти недостаток этой функции, просто используя % s формат и преобразование объекта в строковое представление. C ++ предлагает заметное исключение в том, что он имеет printf функция, унаследованная от своей истории C, но также имеет совершенно другую ввод, вывод механизм, который является предпочтительным.[9]

Уязвимости

Недействительные спецификации преобразования

Если синтаксис спецификации преобразования недопустимы, поведение не определено и может привести к завершению программы. Если их слишком мало аргументы функции предоставляется для предоставления значений для всех спецификаций преобразования в строке шаблона, или, если аргументы не правильного типа, результаты также не определены. Лишние аргументы игнорируются. В ряде случаев неопределенное поведение приводило к "Атака на форматную строку " безопасность уязвимости.

Некоторые компиляторы, например Коллекция компиляторов GNU, будет статически проверять строки формата printf-подобных функций и предупреждать о проблемах (при использовании флагов -Стена или же -Wformat). GCC также будет предупреждать об определяемых пользователем функциях в стиле printf, если нестандартный "формат" __атрибут__ применяется к функции.

Ширина поля по сравнению с явными разделителями в табличном выводе

Использование только ширины полей для табуляции, как в таком формате, как % 8д% 8д% 8д для трех целых чисел в трех 8-символьных столбцах не гарантирует, что разделение полей будет сохранено, если в данных встречаются большие числа. Отсутствие разделения полей может легко привести к повреждению вывода. В системах, которые поощряют использование программ в качестве строительных блоков в сценариях, такие поврежденные данные часто могут быть перенаправлены и повредят дальнейшую обработку, независимо от того, ожидал ли исходный программист, что результат будет прочитан только человеческими глазами. Такие проблемы можно устранить, включив явные разделители, даже пробелы, во все табличные форматы вывода. Просто изменив опасный пример с предыдущего на % 7д% 7д% 7д решает эту проблему, идентифицируя форматирование до тех пор, пока числа не станут больше, но затем явно предотвращает их объединение при выводе из-за явно включенных пробелов. Аналогичные стратегии применимы к строковым данным.

Языки программирования с printf

Языки, в которых используются строки формата, отличные от стиля, описанного в этой статье (например, AMPL и Эликсир ), языки, которые наследуют свою реализацию от JVM или в другой среде (например, Clojure и Scala ), и языки, которые не имеют стандартной собственной реализации printf, но имеют внешние библиотеки, которые имитируют поведение printf (например, JavaScript ) не включены в этот список.

Смотрите также

Рекомендации

  1. ^ «BCPL». www.cl.cam.ac.uk. Получено 19 марта 2018.
  2. ^ Макилрой, М.Д. (1987). Читатель Research Unix: аннотированные выдержки из Руководства программиста, 1971–1986 (PDF) (Технический отчет). CSTR. Bell Labs. 139.
  3. ^ "printf - Справочник по C ++". www.cplusplus.com. Получено 10 июн 2020.
  4. ^ ISO /IEC (1999). ISO / IEC 9899: 1999 (E): Языки программирования - C §7.19.6.1 пункт 7
  5. ^ ""Справочное руководство библиотеки GNU C "," 12.12.3 Таблица преобразований вывода"". Gnu.org. Получено 17 марта 2014.
  6. ^ "printf" (% a добавлено в C99)
  7. ^ «Форматирование вывода числовой печати». Учебники по Java. Oracle Inc. Получено 19 марта 2018.
  8. ^ "Документация по ядру Linux / printk-sizes.txt". Git.kernel.org. Получено 17 марта 2014.
  9. ^ Бьярн Страуструп (1997). Язык программирования C ++ (третье изд.). Эддисон-Уэсли. стр.637–640. ISBN  0-201-88954-4.

внешняя ссылка