Очень многие не знают о существовании в языке C стандартных функций форматированного ввода, и особенно вывода данных. Простейший вывод числа в строчку порой превращается в по-настоящему адовые функции с кромсанием числа на разряды (обычно неэффективно реализованным), выделением памяти под строку и прочими ночными кошмарами.
Но всё это реализовано уже очень давно. Страшно сказать, но функция printf празднует в следующем году 50-летний юбилей! Да этих функций и не могло не быть — на заре развития компьютеров и компиляторов единственным средством общения программы с человеком был текстовый ввод-вывод.
Эти функции лежат в библиотеке stdio, подключим её:
#include "stdio.h"
Итак, функция которая понадобится вам чаще всего — sprintf. Она принимает:
- указатель на строку, в которую будет помещён результат (char *s)
- строку форматирования, например «напряжение: %d, ток: %d»
- перечень параметров, в этом примере — два целых числа. Здесь используются variable arguments.
sprintf работает как шаблонизатор: она читает строку форматирования, находит в ней идентификаторы, подставляет на их место значения переданных параметров и выводит результат в *s.
В строке форматирования может быть несколько идентификаторов, тогда в качестве параметров должно быть передано такое же количество аргументов, и конечно они должны чётко соответствовать типу, указанному в идентификаторе.
Содержание:
Самые частые случаи
Чаще всего вам понадобятся:
- %d — вывод целого числа: sprintf(s, «масса: %d грамм», 2358) => «масса: 2358 грамм»
- %f — вывод дробного числа: sprintf(s, «пинг: %f секунды», 1.432) => «пинг: 1.432 секунды»
- %s — вывод строки: sprintf(s, «json: {\»name\»: \»%s\»}», «joe») => «json: {«name»: «joe»}»
- %02X — вывод байта в виде hex: sprintf(s, «CRC: %02X %02X», 0x43, 0xfe) => «CRC: 43 FE»
Некоторые символы, такие как «, нужно экранировать слешом: \»
Можно добавлять управляющие последовательности, например \n — перевод строки.
Описание
Теперь подробнее.
Каждый идентификатор начинается с символа «%», и сообщает всё о переменной, значение которой нужно подставить: её тип и модификаторы (ширина, точность, размер). Формат идентификатора:
%[флаги][ширина][.точность][размер]тип
Ни один из элементов не является обязательным, кроме типа. Часть элементов относятся только к некоторым типам, например ширина, точность и размер — только к числовым типам.
Между элементами могут стоять пробелы, но делать так нельзя — резко увеличивается вероятность ошибки.
Числовые целые типы
%d/%i, %u, %o, %x/%X — всё это типы целых чисел int (т.е. оно имеет длину равную разрядности платформы).
%d и %i — десятичное целое знаковое число, минус ставится только у отрицательных чисел.
%u — десятичное целое беззнаковое число.
%o — восьмеричное представление int.
%x — шестнадцатиричное представление int в строчном формате: 0..9, a..f.
%X — шестнадцатиричное представление int в прописном формате: 0..9, A..F.
К ним можно применить модификаторы размера:
%l — число интерпретируется как long int = int16_t
%ll — long long int = int32_t
%h — short int
%hh — char
Флаги:
%0d — вывод ведущих нулей, дополняя число до длины, указанной в поле «ширина»
%-d — выравнивание числа по левому краю
И размерность:
%2d — длина числа минимум 2 символа. Если меньше — оно будет дополнено слева пробелами (либо нулями, если есть флаг 0)
Я очень часто использую идентификатор %02X для вывода шестнадцатиричных чисел в пакетах данных, например:
sprintf(s, «%02X %02X %02X», 0xb3, 0x54, 0xaf) => «B3 54 AF»
Типы с плавающей точкой
%f, %e
%f — float
%e — экспоненциальная запись
Модификатор размера только один:
%lf — double
К ним можно применить те же самые флаги 0 и -, а также можно указать ширину и точность:
%.2f — выведет float с двумя знаками после запятой. Если они равны 0 — они всё равно будут выведены, поэтому так можно делать красиво выровненные таблички.
%10.2f — как и в прошлой главе, дополнит число нулями или пробелами до длины 10 символов.
Символы
%c выводит символ с кодом, переданным в качестве параметра.
Строки
%s выводит переданную строку, вплоть до её терминирующего нуля.
Особые типы
%n записывает в переданную переменную счётчик символов — количество символов, которое было выведено на момент появления этого идентификатора.
%p выводит адрес указателя.
%% выводит символ «%»
Варианты функций printf
Мы уже рассмотрели функцию sprintf, как наиболее полезную при программировании под МК — она выводит текст в переданный указатель на строку. Но это не единственная функция из семейства:
- printf(char *format, …) — выводит текст в стандартный вывод (STDIO). Больше применима при программировании для компьютера, но в принципе вы можете переопределить встроенную функцию putc, чтобы она выводила символ как-то по-другому — и сможете, например, выводить через printf напрямую на ЖК-экран.
- snprintf(char *s, int n, char *format, …) — ограничивает количество выведенных символов параметром n, непоместившиеся символы будут потеряны. Очень полезно, если у вас есть какой-то накопительный буфер, который нельзя переполнять.
- fprintf(FILE *f, char *format, …) — выводит текст в файл. Опять же, в основном используется на ПК, но опять же вы можете переопределить её и, например, выводить данные в SD-карту.
- vprintf(char *format, va_list va) — использует перечень аргументов variable arguments. Это просто незаменимая фича при написании своей обёртки вокруг printf.
Все функции вида printf возвращают количество записанных символов.
scanf
Функция, обратная sprintf — это sscanf. Она принимает строку с данными и текстом, пытается её разобрать в соответствии со строкой форматирования, и записывает найденные данные по переданным адресам. Простейший пример:
float lat, lon; sscanf("GPS lat: 63.321, lon: 37.867", "GPS lat: %f, lon: %f", &lat, &lon);
Здесь действуют все те же самые правила составления идентификаторов, что и в printf, и есть все те же самые функции вроде scanf, fscanf, sscanf.