Fscanf форматный ввод из файла


Содержание

Работа с файлами в си ввод и вывод в файл в си

Работа с файлами в си

В этой статье мы узнаем, как считывать данные из файлов и записывать информацию в файлы в программах си. Файлы в си используются для того, чтобы сохранять результат работы программы си и использовать его при новом запуске программы . Например можно сохранять результаты вычислений , статистику игр.
Чтобы работать с файлами в си необходимо подключить библиотеку stdio.h
#include
Чтобы работать с файлом в си необходимо задать указатель на файл по образцу
FILE *имя указателя на файл;
Например
FILE *fin;
Задает указатель fin на файл
Дальше необходимо открыть файл и привязать его к файловому указателю. Для открытия файла в си на чтение используется команда
Имя указателя на файл= fopen(«путь к файлу», «r»);
Например следующая команда
fin = fopen(«C:\\Users\\user\\Desktop\\data.txt», «r»);
откроет файл data.txt, находящийся на рабочем столе по пути C:\\Users\\user\\Desktop Чтобы узнать путь к файлу можно выбрать файл мышью нажать на правую кнопку мыши и выбрать свойства файла. В разделе Расположение будет указан путь к файлу. Обратите внимание , что в си путь указывается с помощью двух слешей.
После работы с файлом в си , необходимо его закрыть с помощью команды
fclose(имя указателя на файл)

Считывание информации из текстового файла в Си

Чтобы можно было считывать русские символы из файла, необходимо настроить работу с Кириллицей с помощью команды
setlocale(LC_ALL, «Russian»);

При этом необходимо в начале программы подключить #include

Чтение форматированного ввода из файла с помощью fscanf и sscanf

Я должен прочитать файл, который имеет эту форму

Я использую эту партию кода (printf добавлен для тестирования):

Я понимаю, что моя проблема — где-то в sscanf (возможно форматирование), потому что кажется, что функция «хранит» всю строку на ключе.

1 ответ

Если вы проверите, например, эту ссылку scanf (и family), вы увидите, что код формата «%s»

соответствует символьной строке (последовательность символов без пробелов)

Таким образом, первый «%s» в вашем формате соответствует всем символам в строке, так как между полями нет пробелов. Совпадение шаблонов не работает.

Вместо этого вы должны использовать формат «%[» :

Вышеуказанная строка формата соответствует всем символам, кроме символа ‘%’ , а затем шаблон соответствует ‘%’ чтобы отменить его, а затем снова соответствует всем, кроме конечного ‘%’ .

Также обратите внимание, что если scanf не соответствует всем вашим форматам, он не будет устанавливать указатели в NULL и даже не может вообще что-либо написать в предоставленные строки (позволяя им не инициализировать). Вместо этого вы должны проверить возвращаемое значение scanf . Он должен соответствовать количеству кодов формата, два в вашем случае, или возникла проблема.

Форматный ввод (scanf)

Функция scanf , обеспечивающая ввод, является аналогом printf ; она выполняет многие из упоминавшихся преобразований, но в противоположном направлении. Ее объявление имеет следующий вид:

int scanf(char *format, …)

Функция scanf читает символы из стандартного входного потока, интерпретирует их согласно спецификациям строки format и рассылает результаты в свои остальные аргументы. Аргумент format мы опишем позже; другие аргументы, каждый из которых должен быть указателем, определяют, где будут запоминаться должным образом преобразованные данные. Как и для printf , в этом параграфе дается сводка наиболее полезных, но отнюдь не всех возможностей данной функции.

Функция scanf прекращает работу, когда оказывается, что исчерпался формат или вводимая величина не соответствует управляющей спецификации. В качестве результата scanf возвращает количество успешно введенных элементов данных. По исчерпании файла она выдает EOF . Существенно то, что значение EOF не равно нулю, поскольку нуль scanf выдает, когда вводимый символ не соответствует первой спецификации форматной строки. Каждое очередное обращение к scanf продолжает ввод символа, следующего сразу за последним обработанным.

Существует также функция sscanf , которая читает из строки (а не из стандартного ввода).

int sscanf(char *string, char *format, arg1, arg2,…)

Функция sscanf просматривает строку string согласно формату format и рассылает полученные значения в arg1 , arg2 и т. д. Последние должны быть указателями.

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

• Пробелы или табуляции, которые игнорируются.

• Обычные символы (исключая %), которые, как ожидается, совпадут с очередными символами, отличными от символов-разделителей входного потока.

• Спецификации преобразования, каждая из которых начинается со знака % и завершается символом-спецификатором типа преобразования. В промежутке между этими двумя символами в любой спецификации могут располагаться, причем в том порядке, как они здесь указаны: знак * (признак подавления присваивания); число, определяющее ширину поля; буква h, l или L, указывающая на размер получаемого значения; и символ преобразования (o, d, x).

Спецификация преобразования управляет преобразованием следующего вводимого поля. Обычно результат помещается в переменную, на которую указывает соответствующий аргумент. Однако если в спецификации преобразования присутствует * , то поле ввода пропускается и никакое присваивание не выполняется. Поле ввода определяется как строка без символов-разделителей; оно простирается до следующего символа-разделителя или же ограничено шириной поля, если она задана. Поскольку символ новой строки относится к символам-разделителям, то sscanf при чтении будет переходить с одной строки на другую. (Символами-разделителями являются символы пробела, табуляции, новой строки, возврата каретки, вертикальной табуляции и перевода страницы. )

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

Перед символами-спецификаторами d , l , o , u и x может стоять буква h , указывающая на то, что соответствующий аргумент должен иметь тип short * (а не int * ),или l (латинская ell), указывающая на тип long * . Аналогично, перед символами-спецификаторами e , f и g может стоять буква l , указывающая, что тип аргумента — double * (а не float * ).

Таблица 7.2 Основные преобразования scanf

Символ Вводимые данные; тип аргумента
d десятичное целое: int *
i целое: int * . Целое может быть восьмеричным (с 0 слева) или шестнадцатеричным (с 0x или 0X слева)
o восьмеричное целое (с нулем слева или без него); int *
u беззнаковое десятичное целое; unsigned int *
x шестнадцатеричное целое (с 0x или 0X слева или без них); int *
c символы; char * . Следующие символы ввода (по умолчанию один) размещаются в указанном месте. Обычный пропуск символов- разделителей подавляется; чтобы прочесть очередной символ, отличный от символа-разделителя, используйте %1s
s Строка символов(без обрамляющих кавычек); char * , указывающая на массив символов, достаточный для строки и завершающего символа ‘\0’, который будет добавлен
e , f , g число с плавающей точкой, возможно, со знаком; обязательно присутствие либо десятичной точки, либо экспоненциальной части, а возможно, и обеих вместе; float *
% сам знак % , никакое присваивание не выполняется

Чтобы построить первый пример, обратимся к программе калькулятора из главы 4, в которой организуем ввод с помощью функции scanf :

Форматированный ввод-вывод данных

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

Функция форматированного ввода вида

fscanf ( fp ,”форматная строка“, &аргумент,…)

выполняет ввод (чтение) данных из открытого файла fp по адресу аргумента. Символы форматной строки задают способ преобразования вводимых байтов. Отдельное преобразуемое поле начинается с текущей позиции в буфере потока. Завершением поля считается символ пробела или перевода строки. Форматная строка обрабатывается слева направо.

Функция форматированного вывода вида

fprintf ( fp, ”форматная строка”, аргумент,…)

выполняет вывод (запись) в открытый файл fp значение аргумента.

Символы форматной строки задают способ, в соответствии с которым выполняется преобразование чисел в последовательность символов. Полученная последовательность символов помещается в специфицированный поток. Все остальные символы строки формата передаются в поток без преобразования.

Пример. Требуется записать в файл (kvadrnum.dat) возведенные в квадрат целые числа, затем прочитать их из файла и вывести на экран. Демонстрируется использование функций файлового и стандартного форматированного ввода-вывода текстовых файлов на диске. При вводе исходных данных осуществляется их проверка.

*ptr=”Массив_чисел_типа_integer_в_файле:”; /*заголовок массива*/

FILE *fp; /* указатель файла */

int first , last, /*начальное и конечное числа */

i, j, /* переменные для циклов */

numbers [256]; /* массив чисел */

clrscr (); /* очистка экрана */

while (1) /* бесконечный цикл ввода */

printf (“Введите конечное число (0..255):”);

/* Проверка корректности ввода чисел: */

if (first >= 0 && last >= first && last

puts ( “Открыт файл (KVADRNUM.DAT) “
” для ввода и вывода квадратов целых чисел.”);

fprintf (fp , “%s\n”, ptr ); /* запись заголовка массива в файл */

for ( i = first ;i

#include /* для команд ввода-вывода */

#include /* для функций clrscr, getch */

char str[50], str1[50], ch; /* символьные массивы и переменная */

FILE *fp; /* файловая переменная-указатель */

clrscr (); /* очистка экрана */

/* Открываем текстовый файл для записи и чтения и заполняем его: */

if ((fp=fopen (“C:\\TC\\MYFILES\\exmfile.txt”, “w+”))==NULL

return 1; /* аварийное завершение программы */

puts (“Запись разнотипных данных в текстовый файл:”);

puts (“Введите целое число:”);

fprintf (fp, “%d”, n); /* запись целого числа в файл */

fflush (stdin); /* очистка буфера ввода */

puts (“Введите символ:”);

putc (ch, fp); /* запись символа в файл */

fflush (stdin); /* очистка буфера ввода */

puts (“Введите строку:”);

fputs (str, fp); /* запись строки в файл */

rewind (fp); /* установка маркера на начало файла */

puts (“\nВывод данных из файла на экран:”);

fscanf (fp, “%d”, &n); /* чтение целого числа из файла */

printf (“n=%d\n”, n); /* вывод целого числа на экран */

ch=getc (fp); /* чтение символа из файла */

printf (“%c\n”,ch); /* вывод символа на экран */

fgets (str1, 50, fp); /* чтение строки из файла */

puts (str1); /* вывод строки на экран */

fclose (fp); /* закрытие файла */

puts (“Нажмите на любую клавишу…”);

getch(); /* задержка экрана результатов */

return 0; /* нормальное завершение программы */

Запись разнотипных данных в текстовый файл:

Введите целое число:

Вывод данных из файла на экран:

Нажмите на любую клавишу…

Не нашли то, что искали? Воспользуйтесь поиском:

Лучшие изречения: Только сон приблежает студента к концу лекции. А чужой храп его отдаляет. 8807 — | 7523 — или читать все.

188.64.174.135 © studopedia.ru Не является автором материалов, которые размещены. Но предоставляет возможность бесплатного использования. Есть нарушение авторского права? Напишите нам | Обратная связь.

Отключите adBlock!
и обновите страницу (F5)

очень нужно

Технология программирования на Си: представление матриц, работа с файлами и с текстами

Работа с файлами

Стандартная библиотека Си содержит набор функций для работы с файлами. Эти функции описаны в стандарте ANSI . Отметим, что файловый ввод-вывод не является частью языка Си , и ANSI -функции — не единственное средство ввода-вывода. Так, в операционной системе Unix более популярен другой набор функций ввода-вывода, который можно использовать не только для работы с файлами, но и для обмена по сети. В C++ часто используются библиотеки классов для ввода-вывода. Тем не менее, функции ANSI -библиотеки поддерживаются всеми Си -компиляторами, и потому программы, применяющие их, легко переносятся с одной платформы на другую. Прототипы функций ввода-вывода и используемые для этого типы данных описаны в стандартном заголовочном файле «stdio.h.

Открытие файла: функция fopen

Для доступа к файлу применяется тип данных FILE . Это структурный тип, имя которого задано с помощью оператора typedef в стандартном заголовочном файле «stdio.h». Программисту не нужно знать, как устроена структура типа файл: ее устройство может быть системно зависимым, поэтому в целях переносимости программ обращаться явно к полям струтуры FILE запрещено. Тип данных «указатель на структуру FILE используется в программах как черный ящик: функция открытия файла возвращает этот указатель в случае успеха, и в дальнейшем все файловые функции применяют его для доступа к файлу.

Прототип функции открытия файла выглядит следующим образом:

Здесь path — путь к файлу (например, имя файла или абсолютный путь к файлу), mode — режим открытия файла. Строка mode может содержать несколько букв. Буква » r » (от слова read) означает, что файл открывается для чтения (файл должен существовать). Буква » w » (от слова write) означает запись в файл, при этом старое содержимое файла теряется, а в случае отсутствия файла он создается. Буква » a » (от слова append) означает запись в конец существующего файла или создание нового файла, если файл не существует.

В некоторых операционных системах имеются различия в работе с текстовыми и бинарными файлами (к таким системам относятся MS DOS и MS Windows; в системе Unix различий между текстовыми и бинарными файлами нет). В таких системах при открытии бинарного файла к строке mode следует добавлять букву » b » (от слова binary), а при открытии текстового файла — букву » t » (от слова text). Кроме того, при открытии можно разрешить выполнять как операции чтения, так и записи; для этого используется символ + (плюс). Порядок букв в строке mode следующий: сначала идет одна из букв » r «, » w «, » a «, затем в произвольном порядке могут идти символы » b «, » t «, » + «. Буквы » b » и » t » можно использовать, даже если в операционной системе нет различий между бинарными и текстовыми файлами, в этом случае они просто игнорируются.

Значения символов в строке mode сведены в следующую таблицу:

r Открыть существующий файл на чтение
w Открыть файл на запись. Старое содержимое файла теряется, в случае отсутствия файла он создаётся.
a Открыть файл на запись. Если файл существует, то запись производится в его конец.
t Открыть текстовый файл.
b Открыть бинарный файл.
+ Разрешить и чтение, и запись.

Несколько примеров открытия файлов:

Обратите внимание, что во втором случае мы используем обычную косую черту / для разделения директорий, хотя в системах MS DOS и MS Windows для этого принято использовать обратную косую черту \ . Дело в том, что в операционной системе Unix и в языке Си, который является для нее родным, символ \ используется в качестве экранирующего символа, т.е. для защиты следующего за ним символа от интерпретации как специального. Поэтому во всех строковых константах Си обратную косую черту надо повторять дважды, как это и сделано в третьем примере. Впрочем, стандартная библиотека Си позволяет в именах файлов использовать нормальную косую черту вместо обратной; эта возможность была использована во втором примере.

Илон Маск рекомендует:  Sqlтета соединение таблиц

В случае удачи функция fopen открытия файла возвращает ненулевой указатель на структуру типа FILE , описывающую параметры открытого файла. Этот указатель надо затем использовать во всех файловых операциях. В случае неудачи (например, при попытке открыть на чтение несуществующий файл) возвращается нулевой указатель. При этом глобальная системная переменная errno, описанная в стандартном заголовочном файле «errno.h, содержит численный код ошибки. В случае неудачи при открытии файла этот код можно распечатать, чтобы получить дополнительную информацию:

Константа NULL

В приведенном выше примере при открытии файла функция fopen в случае ошибки возвращает нулевой указатель на структуру FILE . Чтобы проверить, произошла ли ошибка, следует сравнить возвращенное значение с нулевым указателем. Для наглядности стандартный заголовочный файл «stdio.h» определяет символическую константу NULL как нулевой указатель на тип void :

Сделано это вроде бы с благой целью: чтобы отличить число ноль от нулевого указателя. При этом язык Си, в котором контроль ошибок осуществляется недостаточно строго, позволяет сравнивать указатель общего типа void * с любым другим указателем. Между тем,в Си вместо константы NULL всегда можно использовать просто 0 , и вряд ли от этого программа становится менее понятной. Более строгий язык C++ запрещает сравнение разных указателей, поэтому в случае C++ стандартный заголовочный файл определяет константу NULL как обычный ноль:

Автор языка C++ Б. Страуструп советует использовать обычный ноль 0 вместо символического обозначения NULL . Тем не менее, по традиции большинство программистов любят константу NULL .

Константа NULL не является частью языка Си или C++, и без подключения одного из стандартных заголовочных файлов, в котором она определяется, использовать ее нельзя. (По этой причине авторы языка Java добавили в язык ключевое слово null , записываемое строчными буквами.) Так что в случае Си или C++ безопаснее следовать совету Б. Страуструпа и использовать обычный ноль 0 вместо символической константы NULL .

Диагностика ошибок: функция perror

Использовать переменную errno для печати кода ошибки не очень удобно, поскольку необходимо иметь под рукой таблицу возможных кодов ошибок и их значений. В стандартной библиотеке Си существует более удобная функция perror , которая печатает системное сообщение о последней ошибке вместо ее кода. Печать производится на английском языке, но есть возможность добавить к системному сообщению любой текст, который указывается в качестве единственного аргумента функции perror . Например, предыдущий фрагмент переписывается следующим образом:

Функция perror печатает сначала пользовательское сообщение об ошибке, затем после двоеточия системное сообщение. Например, при выполнении приведенного фрагмента в случае ошибки из-за отсутствия файла будет напечатано

Функции бинарного чтения и записи fread и fwrite

После того как файл открыт, можно читать информацию из файла или записывать информацию в файл. Рассмотрим сначала функции бинарного чтения и записи fread и fwrite . Они называются бинарными потому, что не выполняют никакого преобразования информации при вводе или выводе (с одним небольшим исключением при работе с текстовыми файлами, которое будет рассмотрено ниже): информация хранится в файле как последовательность байтов ровно в том виде, в котором она хранится в памяти компьютера.

Функция чтения fread имеет следующий прототип:

Здесь size_t определен как беззнаковый целый тип в системных заголовочных файлах. Функция пытается прочесть numElems элементов из файла, который задается указателем f на структуру FILE , размер каждого элемента равен elemSize . Функция возвращает реальное число прочитанных элементов, которое может быть меньше, чем numElems , в случае конца файла или ошибки чтения. Указатель f должен быть возвращен функцией fopen в результате успешного открытия файла. Пример использования функции fread :

В этом примере файл » tmp.dat » открывается на чтение как бинарный, из него читается 100 вещественных чисел размером 8 байт каждое. Функция fread возвращает реальное количество прочитанных чисел, которое меньше или равно, чем 100.

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

Внимание! Открытие файла как текстового с помощью функции fopen , например,

вовсе не означает, что числа при вводе с помощью функции fopen будут преобразовываться из текстовой формы в бинарную! Из этого следует только то, что в операционных системах, в которых строки текстовых файлов разделяются парами символами » \r\n » (они имеют названия CR и LF — возврат каретки и продергивание бумаги, Carriage Return и Line Feed ), при вводе такие пары символов заменяются на один символ » \n » (продергивание бумаги). Обратно, при выводе символ » \n » заменяется на пару » \r\n «. Такими операционными системами являются MS DOS и MS Windows. В системе Unix строки разделяются одним символом » \n » (отсюда проистекает обозначение » \n «, которое расшифровывается как new line). Таким образом, внутреннее представление текста всегда соответствует системе Unix, а внешнее — реально используемой операционной системе. Отметим также, что создатели операционной системы компьютеров Apple Macintosh выбрали, чтобы жизнь не казалась скучной, третий, отличный от двух предыдущих, вариант: текстовые строки разделяются одним символом » \r » возврат каретки!

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

Функция бинарной записи в файл fwrite аналогична функции чтения fread . Она имеет следующий прототип:

Функция возвращает число реально записанных элементов, которое может быть меньше, чем numElems , если при записи произошла ошибка — например, не хватило свободного пространства на диске. Пример использования функции fwrite :

Закрытие файла: функция fclose

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

Для закрытия файла используется функция fclose с прототипом

В случае успеха функция fclose возвращает ноль, при ошибке — отрицательное значение (точнее, константу конец файла EOF, определенную в системных заголовочных файлах как минус единица). При ошибке можно воспользоваться функцией perror , чтобы напечатать причину ошибки. Отметим, что ошибка при закрытии файла — явление очень редкое (чего не скажешь в отношении открытия файла), так что анализировать значение, возвращаемое функцией fclose , в общем-то, не обязательно. Пример использования функции fclose :

Пример: подсчет числа символов и строк в текстовом файле

В качестве содержательного примера использования рассмотренных выше функций файлового ввода приведем программу, которая подсчитывает число символов и строк в текстовом файле. Программа сначала вводит имя файла с клавиатуры. Для этого используется функция scanf ввода по формату из входного потока, для ввода строки применяется формат » %s . Затем файл открывается на чтение как бинарный (это означает, что при чтении не будет происходить никакого преобразования разделителей строк). Используя в цикле функцию чтения fread , мы считываем содержимое файла порциями по 512 байтов, каждый раз увеличивая суммарное число прочитанных символов. После чтения очередной порции сканируется массив прочитанных символов и подсчитывается число символов » \n » продергивания бумаги, которые записаны в концах строк текстовых файлов как в системе Unix, так и в MS DOS или MS Windows. В конце закрывается файл и печатается результат.

Пример выполнения программы: она применяется к собственному тексту, записанному в файле «wc.cpp.

Форматный ввод-вывод: функции fscanf и fprintf

В отличие от функции бинарного ввода fread , которая вводит байты из файла без всякого преобразования непосредственно в память компьютера, функция форматного ввода fscanf предназначена для ввода информации с преобразованием ее из текстового представления в бинарное. Пусть информация записана в текстовом файле в привычном для человека виде (т.е. так, что ее можно прочитать или ввести в файл, используя текстовый редактор). Функция fscanf читает информацию из текстового файла и преобразует ее во внутреннее представление данных в памяти компьютера. Информация о количестве читаемых элементов, их типах и особенностях представления задается с помощью формата. В случае функции ввода формат — это строка, содержащая описания одного или нескольких вводимых элементов. Форматы, используемые функцией fscanf , аналогичны применяемым функцией scanf , они уже неоднократно рассматривались (см. раздел 3.5.4). Каждый элемент формата начинается с символа процента » % «. Наиболее часто используемые при вводе форматы приведены в таблице:

%d целое десятичное число типа int (d — от decimal)
%lf вещ. число типа double (lf — от long float)
%c один символ типа char
%s ввод строки. Из входного потока выделяется слово, ограниченное пробелами или символами перевода строки ‘\n’. Слово помещается в массив символов. Конец слова отмечается нулевым байтом.

Прототип функции fscanf выглядит следующим образом:

Многоточие здесь означает, что функция имеет переменное число аргументов, большее двух, и что количество и типы аргументов, начиная с третьего, произвольны. На самом деле, фактические аргументы, начиная с третьего, должны быть указателями на вводимые переменные. Несколько примеров использования функции fscanf :

Функция fscanf возвращает число успешно введенных элементов. Таким образом, возвращаемое значение всегда меньше или равно количеству процентов внутри форматной строки (которое равно числу фактических аргументов минус 2).

Функция fprintf используется для форматного вывода в файл. Данные при выводе преобразуются в их текстовое представление в соответствии с форматной строкой. Ее отличие от форматной строки, используемой в функции ввода fscanf , заключается в том, что она может содержать не только форматы для преобразования данных, но и обычные символы, которые записываются без преобразования в файл. Форматы, как и в случае функции fscanf , начинаются с символа процента » % «. Они аналогичны форматам, используемым функцией fscanf . Небольшое отличие заключается в том, что форматы функции fprintf позволяют также управлять представлением данных, например, указывать количество позиций, отводимых под запись числа, или количество цифр после десятичной точки при выводе вещественного числа. Некоторые типичные примеры форматов для вывода приведены в следующей таблице:

%d вывод целого десятичного числа
%10d вывод целого десятичного числа, для записи числа отводится 10 позиций, запись при необходимости дополняется пробелами слева
%lf вывод вещественного число типа double в форме с фиксированной десятичной точкой
%.3lf вывод вещественного число типа double с печатью трёх знаков после десятичной точки
%12.3lf вывод вещественного число типа double с тремя знаками после десятичной точки, под число отводится 12 позиций
%c вывод одного символа
%s конец строки, т.е. массива символов. Конец строки задается нулевым байтом

Прототип функции fprintf выглядит следующим образом:

int fprintf(FILE *f, const char *format, . );

Многоточие, как и в случае функции fscanf , означает, что функция имеет переменное число аргументов. Количество и типы аргументов, начиная с третьего, должны соответствовать форматной строке. В отличие от функции fscanf , фактические аргументы, начиная с третьего, представляют собой выводимые значения, а не указатели на переменные. Для примера рассмотрим небольшую программу, выводящую данные в файл «tmp.dat»:

В результате выполнения этой программы в файл «tmp.dat» будет записан следующий текст:

В последнем примере форматная строка содержит внутри себя двойные апострофы. Это специальные символы, выполняющие роль ограничителей строки, поэтому внутри строки их надо экранировать (т.е. защищать от интерпретации как специальных символов) с помощью обратной косой черты \ , которая, напомним, в системе Unix и в языке Си выполняет роль защитного символа. Отметим также, что мы воспользовались стандартной функцией sqrt , вычисляющей квадратный корень числа, и стандартной функцией strlen , вычисляющей длину строки.

Понятие потока ввода или вывода

В операционной системе Unix и в других системах, использующих идеи системы Unix (например, MS DOS и MS Windows), применяется понятие потока ввода или вывода. Поток представляет собой последовательность байтов. Различают потоки ввода и вывода. Программа может читать данные из потока ввода и выводить данные в поток вывода. Программы можно запускать в конвейере, когда поток вывода первой программы является потоком ввода второй программы и т.д. Для запуска двух программ в конвейере используется символ вертикальной черты | между именами программ в командной строке. Например, командная строка

означает, что поток вывода программы ab направляется на вход программе cd , а поток вывода программы cd — на вход программе ef . По умолчанию, потоком ввода для программы является клавиатура, поток вывода назначен на терминал (или, как говорят программисты, на консоль). Потоки можно перенаправлять в файл или из файла, используя символы больше > и меньше , которые можно представлять как воронки. Например, командная строка

перенаправляет выходной поток программы abcd в файл «tmp.res», т.е. данные будут выводиться в файл вместо печати на экране терминала. Соответственно, командная строка

заставляет программу abcd читать исходные данные из файла «tmp.dat» вместо ввода с клавиатуры. Командная строка

перенаправляет как входной, так и выходной потоки: входной назначается на файл «tmp.dat», выходной — на файл «tmp.res».

В Си работа с потоком не отличается от работы с файлом. Доступ к потоку осуществляется с помощью переменной типа FILE * . В момент начала работы Си-программы открыты три потока:

  • stdin — стандартный входной поток. По умолчанию он назначен на клавиатуру;
  • stdout — стандартный выходной поток. По умолчанию он назначен на экран терминала;
  • stderr — выходной поток для печати информации об ошибках. Он также назначен по умолчанию на экран терминала.

Переменные stdin , stdout , stderr являются глобальными, они описаны в стандартном заголовочном файле «stdio.h. Операции файлового ввода-вывода могут использовать эти потоки, например, строка

вводит значение целочисленной переменной n из входного потока. Строка

выводит значение переменой n в выходной поток. Строка

выводит указанный текст в поток stderr , используемый обычно для печати сообщений об ошибках. Функция perror также выводит сообщения об ошибках в поток stderr .

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

обычный вывод программы abcd будет записываться в файл «tmp.res», а сообщения об ошибках по-прежнему будут печататься на экране терминала. Для того чтобы перенаправить в файл «tmp.log» стандартный поток печати ошибок, следует использовать командную строку

(между двойкой и символом > не должно быть пробелов!). Двойка здесь означает номер перенаправляемого потока. Стандартный входной поток имеет номер 0, стандартный выходной поток — номер 1, стандартный поток печати ошибок — номер 2. Данная команда перенаправляет только поток stderr , поток stdout по-прежнему будет выводиться на терминал. Можно перенаправить потоки в разные файлы:

Илон Маск рекомендует:  Что такое код domdocument >create_element

Таким образом, существование двух разных потоков вывода позволяет при необходимости отделить мух от котлет, т.е. направить нормальный вывод и вывод информации об ошибках в разные файлы.

Функции scanf и printf ввода и вывода в стандартные потоки

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

ФОРМАТИРОВАННЫЙ ВВОД И ФУНКЦИЯ SCANF

Функция scanf, обеспечивающая ввод, является аналогом printf; она выполняет многие из упоминавшихся преобразований, но в противоположном направлении. Ее объявление имеет следующий вид:

int scanf(char *format, . )

Функция scanf читает символы из стандартного входного потока, интерпретирует их согласно спецификациям строки format и рассылает результаты в свои остальные аргументы. Аргумент format мы опишем позже; другие аргументы, каждый из которых должен быть указателем, определяют, где будут запоминаться должным образом преобразованные данные. Как и для printf, в этом параграфе дается сводка наиболее полезных, но отнюдь не всех возможностей данной функции.

Функция scanf прекращает работу, когда оказывается, что исчерпался формат или вводимая величина не соответствует управляющей спецификации. В качестве результата scanf возвращает количество успешно введенных элементов данных. По исчерпании файла она выдает EOF. Существенно то, что значение EOF не равно нулю, поскольку нуль scanf выдает, когда вводимый символ не соответствует первой спецификации форматной строки. Каждое очередное обращение к scanfпродолжает ввод символа, следующего сразу за последним обработанным.

Существует также функция sscanf, которая читает из строки (а не из стандартного ввода).

int sscanf(char *string, char *format, arg1, arg2, . )

Функция sscanf просматривает строку string согласно формату format и рассылает полученные значения в arg1, arg2 и т. д. Последние должны быть указателями.

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

 Пробелы или табуляции, которые игнорируются.

 Обычные символы (исключая %), которые, как ожидается, совпадут с очередными символами, отличными от символов-разделителей входного потока.

 Спецификации преобразования, каждая из которых начинается со знака % и завершается символом-спецификатором типа преобразования. В промежутке между этими двумя символами в любой спецификации могут располагаться, причем в том порядке, как они здесь указаны: знак * (признак подавления присваивания); число, определяющее ширину поля; буква h, l или L, указывающая на размер получаемого значения; и символ преобразования (o, d, x).

Спецификация преобразования управляет преобразованием следующего вводимого поля. Обычно результат помещается в переменную, на которую указывает соответствующий аргумент. Однако если в спецификации преобразования присутствует *, то поле ввода пропускается и никакое присваивание не выполняется. Поле ввода определяется как строка без символов-разделителей; оно простирается до следующего символа-разделителя или же ограничено шириной поля, если она задана. Поскольку символ новой строки относится к символам- разделителям, то sscanf при чтении будет переходить с одной строки на другую. (Символами-разделителями являются символы пробела, табуляции, новой строки, возврата каретки, вертикальной табуляции и перевода страницы.)

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

Перед символами-спецификаторами d, l, o, u и x может стоять буква h, указывающая на то, что соответствующий аргумент должен иметь тип short * (а не int *),или l (латинская ell), указывающая на тип long *. Аналогично, перед символами-спецификаторами e, f и g может стоять буква l, указывающая, что тип аргумента — double * (а не float *).

Таблица 7.2 Основные преобразования scanf

Форматный ввод — функция SCANF

Осуществляющая ввод функция SCANF является аналогомPRINTF и позволяет проводить в обратном направлении многиеиз тех же самых преобразований. Функция SCANF(CONTROL, ARG1, ARG2, . ) читает символы из стандартного ввода, интерпретирует их всоответствии с форматом, указанном в аргументе CONTROL, ипомещает результаты в остальные аргументы. Управляющий аргу-мент описывается ниже; другие аргументы, каждый из которыхдолжен быть указателем, определяют, куда следует поместитьсоответствующим образом преобразованный ввод. Управляющая строка обычно содержит спецификации преобра-зования, которые используются для непосредственной интерпре-тации входных последовательностей. Управляющая строка можетсодержать:- пробелы, табуляции или символы новой строки («символы пус- тых промежутков»), которые игнорируются. — Обычные символы (не %), которые предполагаются совпадающи- ми со следующими отличными от символов пустых промежутков символами входного потока.- Спецификации преобразования, состоящие из символа %, нео- бязательного символа подавления присваивания *, необяза- тельного числа, задающего максимальную ширину поля и сим- вола преобразования. Спецификация преобразования управляет преобразованиемследующего поля ввода. нормально результат помещается в пе-ременную, которая указывается соответствующим аргументом.Если, однако , с помощью символа * указано подавление прис-ваивания, то это поле ввода просто пропускается и никакогоприсваивания не производится. Поле ввода определяется какстрока символов, которые отличны от символов простых проме-жутков; оно продолжается либо до следующего символа пустогопромежутка, либо пока не будет исчерпана ширина поля, еслиона указана. Отсюда следует, что при поиске нужного ей вво-да, функция SCANF будет пересекать границы строк, посколькусимвол новой строки входит в число пустых промежутков. Символ преобразования определяет интерпретацию поля вво-да; согласно требованиям основанной на вызове по значениюсемантики языка «с» соответствующий аргумент должен бытьуказателем. Допускаются следующие символы преобразования:D — на вводе ожидается десятичное целое; соответствующий ар- гумент должен быть указателем на целое.O — На вводе ожидается восьмеричное целое (с лидирующим ну- лем или без него); соответствующий аргумент должен быть указателем на целое.X — На вводе ожидается шестнадцатеричное целое (с лидирующи- ми 0X или без них); соответствующий аргумент должен быть указателем на целое.H — На вводе ожидается целое типа SHORT; соответсвующий ар- гумент должен быть указателем на целое типа SHORT.C — Ожидается отдельный символ; соответствующий аргумент должен быть указателем на символы; следующий вводимый символ помещается в указанное место. Обычный пропуск сим- волов пустых промежутков в этом случае подавляется; для чтения следующего символа, который не является символом пустого промежутка, пользуйтесь спецификацией преобразо- вания %1S.S — Ожидается символьная строка; соответствующий аргумент должен быть указателем символов, который указывает на массив символов, который достаточно велик для принятия строки и добавляемого в конце символа \0.F — Ожидается число с плавающей точкой; соответствующий ар- гумент должен быть указателем на переменную типа FLOAT.Е — символ преобразования E является синонимом для F. Формат ввода переменной типа FLOAT включает необязательный знак, строку цифр, возможно содержащую десятичную точку и нео- бязательное поле экспоненты, состоящее из буквы E, за ко- торой следует целое, возможно имеющее знак. Перед символами преобразования D, O и X может стоять L,которая означает , что в списке аргументов должен находитьсяуказатель на переменную типа LONG, а не типа INT. Аналогич-но, буква L может стоять перед символами преобразования Eили F, говоря о том, что в списке аргументов должен нахо-диться указатель на переменную типа DOUBLE, а не типа FLOAT. Например, обращениеINT I;FLOAT X;CHAR NAME[50];SCANF(«&D %F %S», &I, &X, NAME); со строкой на вводе 25 54.32E-1 THOMPSON приводит к присваиванию I значения 25,X — значения 5.432 иNAME — строки «THOMPSON», надлежащим образом законченнойсимволом \ 0. эти три поля ввода можно разделить столькимипробелами, табуляциями и символами новых строк, сколько выпожелаете. Обращение INT I; FLOAT X; CHAR NAME[50]; SCANF(«%2D %F %*D %2S», &I, &X, NAME); с вводом 56789 0123 45A72 присвоит I значение 56, X — 789.0, пропустит 0123 и поместитв NAME строку «45». при следующем обращении к любой процеду-ре ввода рассмотрение начнется с буквы A. В этих двух приме-рах NAME является указателем и, следовательно, перед ним ненужно помещать знак &. В качестве другого примера перепишем теперь элементарныйкалькулятор из главы 4, используя для преобразования вводафункцию SCANF: #INCLUDE MAIN() /* RUDIMENTARY DESK CALCULATOR */ \( DOUBLE SUM, V; SUM =0; WHILE (SCANF(«%LF», &V) !=EOF) PRINTF(«\T%.2F\N», SUM += V); \) выполнение функции SCANF заканчивается либо тогда, когда онаисчерпывает свою управляющую строку, либо когда некоторыйэлемент ввода не совпадает с управляющей спецификацией. Вкачестве своего значения она возвращает число правильно сов-падающих и присвоенных элементов ввода. Это число может быть использовано для определения количества найденных элементовввода. при выходе на конец файла возвращается EOF; подчерк-нем, что это значение отлично от 0, что следующий вводимыйсимвол не удовлетворяет первой спецификации в управляющейстроке. При следующем обращении к SCANF поиск возобновляетсянепосредственно за последним введенным символом. Заключительное предостережение: аргументы функции SCANFдолжны быть указателями. Несомненно наиболее распространен-ная ошибка состоит в написании SCANF(«%D», N); вместо SCANF(«%D», &N);

7.5. Форматное преобразование в памяти

От функции SCANF и PRINTF происходят функции SSCANF иSPRINTF, которые осуществляют аналогичные преобразования, нооперируют со строкой, а не с файлом. Обращения к этим функ-циям имеют вид: SPRINTF(STRING, CONTROL, ARG1, ARG2, . ) SSCANF(STRING, CONTROL, ARG1, ARG2, . ) Как и раньше , функция SPRINTF преобразует свои аргументыARG1, ARG2 и т.д. В соответствии с форматом, указанным вCONTROL, но помещает результаты в STRING, а не в стандартныйвывод. KОнечно, строка STRING должна быть достаточно велика,чтобы принять результат. Например, если NAME — это символь-ный массив, а N — целое, то SPRINTF(NAME, «TEMP%D», N); создает в NAME строку вида TEMPNNN, где NNN — значение N. Функция SSCANF выполняет обратные преобразования — онапросматривает строку STRING в соответствии с форматом в ар-гументе CONTROL и помещает результирующие значения в аргу-менты ARG1, ARG2 и т.д.эти аргументы должны быть указателя-ми. В результате обращения SSCANF(NAME, «TEMP%D», &N); переменная N получает значение строки цифр, следующих заTEMP в NAME. Упражнение 7-2 ————— Перепишите настольный калькулятор из главы 4, используядля ввода и преобразования чисел SCANF и/или SSCANF.

Доступ к файлам

Все до сих пор написанные программы читали из стандарт-ного ввода и писали в стандартный вывод, относительно кото-рых мы предполагали, что они магическим образом предоставле-ны программе местной операционной системой. Следующим шагом в вопросе ввода-вывода является написа-ние программы, работающей с файлом, который не связан зара-нее с программой. одной из программ, которая явно демонстри-рует потребность в таких операциях, является CAT, котораяобъединяет набор из нескольких именованных файлов в стандар-тный вывод. Программа CAT используется для вывода файлов натерминал и в качестве универсального сборщика ввода дляпрограмм, которые не имеют возможности обращаться к файлампо имени. Например, команда CAT X.C.Y.C печатает содержимое файлов X.C и Y.C в стандартный вывод. Вопрос состоит в том, как организовать чтение из имено-ванных файлов, т.е., как связать внешние имена, которымимыслит пользователь, с фактически читающими данные операто-рами. Эти правила просты. Прежде чем можно считывать из неко-торого файла или записывать в него, этот файл должен бытьоткрыт с помощью функции FOPEN из стандартной библиотеки.функция FOPEN берет внешнее имя (подобное X.C или Y.C), про-водит некоторые обслуживающие действия и переговоры с опера-ционной системой (детали которых не должны нас касаться) ивозвращает внутреннее имя, которое должно использоваться припоследующих чтениях из файла или записях в него. Это внутреннее имя, называемое «указателем файла», фак-тически является указателем структуры, которая содержит ин-формацию о файле, такую как место размещения буфера, текущаяпозиция символа в буфере, происходит ли чтение из файла илизапись в него и тому подобное. Пользователи не обязаны знатьэти детали, потому что среди определений для стандартноговвода-вывода, получаемых из файла STDIO.H, содержится опре-деление структуры с именем FILE. Единственное необходимоедля указателя файла описание демонстрируется примером: FILE *FOPEN(), *FP; Здесь говорится, что FP является указателем на FILE иFOPEN возвращает указатель на FILE. Oбратите внимание, чтоFILE является именем типа, подобным INT, а не ярлыку струк-туры; это реализовано как TYPEDEF. (Подробности того, каквсе это работает на системе UNIX, приведены в главе 8). Фактическое обращение к функции FOPEN в программе имеетвид: FP=FOPEN(NAME,MODE); Первым аргументом функции FOPEN является «имя» файла, кото-рое задается в виде символьной строки. Второй аргумент MODE(«режим») также является символьной строкой, которая указы-вает, как этот файл будет использоваться. Допустимыми режи-мами являются: чтение («R»), запись («W») и добавление(«A»). Если вы откроете файл, который еще не сущетвует, для за- писи или добавления, то такой файл будет создан (если этовозможно). Открытие существующего файла на запись приводит котбрасыванию его старого содержимого. Попытка чтения несу-ществующего файла является ощибкой. Ошибки могут быть обус- ловлены и другими причинами (например, попыткой чтения из файла, не имея на то разрешения). При наличии какой-либо ошибки функция возвращает нулевое значение указателя NULL(которое для удобства также определяется в файле STDIO.H). Другой необходимой вещью является способ чтения или за-писи, если файл уже открыт. Здесь имеется несколько возмож-ностей, из которых GETC и PUTC являются простейшими.функцияGETC возвращает следующий символ из файла; ей необходим ука-затель файла, чтобы знать, из какого файла читать. Таким об- разом, C=GETC(FP) помещает в «C» следующий символ из файла, указанного посред-ством FP, и EOF, если достигнут конец файла. Функция PUTC, являющаяся обращением к функции GETC, PUTC(C,FP) помещает символ «C» в файл FP и возвращает «C». Подобно фун-кциям GETCHAR и PUTCHAR, GETC и PUTC могут быть макросами, ане функциями. При запуске программы автоматически открываются три фай-ла, которые снабжены определенными указателями файлов. Этимифайлами являются стандартный ввод, стандартный вывод и стан-дартный вывод ошибок; соответствующие указатели файлов назы-ваются STDIN, STDOUT и STDERR. Обычно все эти указатели свя-заны с терминалом, но STDIN и STDOUT могут быть перенаправ-лены на файлы или в поток (PIPE), как описывалось в разделе7.2. Функции GETCHAR и PUTCHAR могут быть определены в терми-налах GETC, PUTC, STDIN и STDOUT следующим образом:#DEFINE GETCHAR() GETC(STDIN) #DEFINE PUTCHAR(C) PUTC(C,STDOUT)При работе с файлами для форматного ввода и вывода можно ис-пользовать функции FSCANF и FPRINTF. Они идентичны функциямSCANF и PRINTF, за исключением того, что первым аргументомявляется указатель файла, определяющий тот файл, который бу-дет читаться или куда будет вестись запись; управляющаястрока будет вторым аргументом. Покончив с предварительными замечаниями, мы теперь всостоянии написать программу CAT для конкатенации файлов.Используемая здесь основная схема оказывается удобной вомногих программах: если имеются аргументы в командной стро-ке, то они обрабатываются последовательно. Если такие аргу-менты отсутствуют, то обрабатывается стандартный ввод. Этопозволяет использовать программу как самостоятельно, так икак часть большей задачи. #INCLUDE MAIN(ARGC, ARGV) /*CAT: CONCATENATE FILES*/ INT ARGC; CHAR *ARGV[]; \( FILE *FP, *FOPEN(); IF(ARGC==1) /*NO ARGS; COPY STANDARD INPUT*/ FILECOPY(STDIN); ELSE WHILE (—ARGC > 0) IF ((FP=FOPEN(*++ARGV,»R»))==NULL) \( PRINTF(«CAT:CAN’T OPEN %\N»,*ARGV); BREAK; \) ELSE \( FILECOPY(FP); FCLOSE(FP); \) \) FILECOPY(FP) /*COPY FILE FP TO STANDARD OUTPUT*/ FILE *FP; \( INT C; WHILE ((C=GETC(FP)) !=EOF) PUTC(C, STDOUT); \) Указатели файлов STDIN и STDOUT заранее определены в библио-теке ввода-вывода как стандартный ввод и стандартный вывод;они могут быть использованы в любом месте, где можно исполь-зовать объект типа FILE*.они однако являются константами, ане переменными, так что не пытайтесь им что-либо присваи-вать. Функция FCLOSE является обратной по отношению к FOPEN;она разрывает связь между указателем файла и внешним именем,установленную функцией FOPEN, и высвобождает указатель файладля другого файла.большинство операционных систем имеют не-которые ограничения на число одновременно открытых файлов,которыми может распоряжаться программа. Поэтому, то как мыпоступили в CAT, освободив не нужные нам более объекты, яв-ляется хорошей идеей. Имеется и другая причина для примене-ния функции FCLOSE к выходному файлу — она вызывает выдачуинформации из буфера, в котором PUTC собирает вывод. (Принормальном завершении работы программы функция FCLOSE вызы-вается автоматически для каждого открытого файла).

Илон Маск рекомендует:  Что такое код imagepsbbox

Последнее изменение этой страницы: 2020-08-26; Нарушение авторского права страницы

Функции форматированного ввода/вывода: printf и scanf

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

Но всё это реализовано уже очень давно. Страшно сказать, но функция printf празднует в следующем году 50-летний юбилей! Да этих функций и не могло не быть — на заре развития компьютеров и компиляторов единственным средством общения программы с человеком был текстовый ввод-вывод.

Эти функции лежат в библиотеке stdio, подключим её:

Итак, функция которая понадобится вам чаще всего — sprintf. Она принимает:

  1. указатель на строку, в которую будет помещён результат (char *s)
  2. строку форматирования, например «напряжение: %d, ток: %d»
  3. перечень параметров, в этом примере — два целых числа. Здесь используются 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

Флаги:

%0d — вывод ведущих нулей, дополняя число до длины, указанной в поле «ширина»

%-d — выравнивание числа по левому краю

И размерность:

%2d — длина числа минимум 2 символа. Если меньше — оно будет дополнено слева пробелами (либо нулями, если есть флаг 0)

Я очень часто использую идентификатор %02X для вывода шестнадцатиричных чисел в пакетах данных, например:

sprintf(s, «%02X %02X %02X», 0xb3, 0x54, 0xaf) => «B3 54 AF»

Типы с плавающей точкой

%e — экспоненциальная запись

Модификатор размера только один:

К ним можно применить те же самые флаги 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. Она принимает строку с данными и текстом, пытается её разобрать в соответствии со строкой форматирования, и записывает найденные данные по переданным адресам. Простейший пример:

Здесь действуют все те же самые правила составления идентификаторов, что и в printf, и есть все те же самые функции вроде scanf, fscanf, sscanf.

Форматированный ввод и вывод

Форматированный вывод

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

Функция форматированного вывода printf получает в качестве аргументов строку формат и аргументы, которые необходимо вывести в соответствии с форматом, и возвращает число выведенных символов. В случае ошибки возвращает отрицательное значение и устанавливает значение ferror. Если произошло несколько ошибок, errno равно EILSEQ.
int printf (const char * format, . );

Функция проходит по строке и заменяет первое вхождение % на первый аргумент, второе вхождение % на второй аргумент и т.д. Далее мы будем просто рассматривать список флагов и примеры использования.

Общий синтаксис спецификатора формата
%[флаги][ширина][.точность][длина]спецификатор
Спецификатор – это самый важный компонент. Он определяет тип переменной и способ её вывода.

Таб. 1 Спецификатор типа.

Спецификатор Что хотим вывести Пример
d или i Целое со знаком в в десятичном виде 392
u Целое без знака в десятичном виде 7235
o Беззнаковое в восьмеричном виде 657
x Беззнаковое целое в шестнадцатеричном виде 7fa
X Беззнаковое целое в шестнадцатеричном виде, верхний регистр 7FA
f или F Число с плавающей точкой 3.4563745
e Экспоненциальная форма для числа с плавающей точкой 3.1234e+3
E Экспоненциальная форма для числа с плавающей точкой, верхний регистр 3.1234E+3
g Кратчайшее из представлений форматов f и e 3.12
G Кратчайшее из представлений форматов F и E 3.12
a Шестнадцатеричное представление числа с плавающей точкой -0xc.90fep-2
A Шестнадцатеричное представление числа с плавающей точкой, верхний регистр -0xc.90FEP-2
c Буква a
s Строка (нуль-терминированный массив букв) Hello World
p Адрес указателя b8000000
n Ничего не пачатает. Аргументом должен быть указатель на signed int. По этому адресу будет сохранено количество букв, которое было выведено до встречи %n
% Два идущих друг за другом процента выводят знак процента %

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

Таб. 2 Флаги.

Флаг Описание
Выключка влево на заданное шириной значение
+ Явно указывать знак у числа, даже для положительных чисел
(пробел) Если знак не будет выведен, то вставляет пробел перед выводимым числом
# Когда используется вместе с o, x или X, вставляет перед числом 0, 0x или 0X
Когда используется со спецификаторами a, A, e, E, f, F, g или G, вставляет десятичную точку, даже если после неё нет десятичных знаков.
Вставляет нули, когда объявлен спецификатор ширины
Таб. 3 Ширина.
Ширина Описание
(число) Минимальное количество знаков, которое необходимо вывести. Если в числе меньше знаков, то вставляет пробелы (или нули)
* Ширина не указана в строке формата, она передаётся отдельно в виде аргумента, который должен предшествовать выводимому числу
Таб. 4 Точность.
.Точность Описание
.число Для спецификаторов целых (d, i, o, u, x, X) точность определяет минимальное количество знаков, которое необходимо вывести. Если значение короче, то выводятся нули перед числом. Значение не обрезается, даже если оно длиннее. Точночть 0 означает, что для значения 0 ничего не выводится.
Для спецификаторов чисел с плавающей точкой (a, A, e, E, f, F) это число знаков, которые необходимо вывести после десятичной точки (по умолчанию 6).
Для g и G — это число значащих разрядов, которые необходимо вывести.
Для s — выводится указанное число символов. По умолчанию выводятся все символы до первого нулевого.
Если число не стоит, то по умолчанию точность равна 0
.* Точность не указана в строке формата, она передаётся отдельно в виде аргумента, который должен предшествовать выводимому числу

Суб-спецификатор длины изменяет длину типа. В случае, если длина не совпадает с типом, по возможности происходит преобразование до нужного типа.

Таб. 5 Длина.

спецификаторы
Длина d, i u o x X f F e E g G a A c s p n
(none) int unsigned int double int char* void* int*
hh signed char unsigned char signed char*
h short int unsigned short int short int*
l long int unsigned long int wint_t wchar_t* long int*
ll long long int unsigned long long int long long int*
j intmax_t uintmax_t intmax_t*
z size_t size_t size_t*
t ptrdiff_t ptrdiff_t ptrdiff_t*
L long double

Форматированный ввод

Рассмотрим форматированный ввод функцией scanf.
int scanf(const char*, . )
Функция принимает строку формата ввода (она похожа на строку формата printf) и адреса, по которым необходимо записать считанные данные. Возвращает количество успешно проинициализированных аргументов.
Формат спецификатора ввода
%[*][ширина][длинна]спецификатор

Таб. 6 Спецификатор типа.

Спецификатор Описание Выбранные символы
i, u Целые Произвольное число цифр (0-9), возможно, начинающихся с + или -. Если число начинается с 0, то считывается в восьмеричном формате, если с 0x, то в шестнадцатеричном.
d Десятичное целое Произвольное число цифр (0-9), возможно, начинающихся с + или -.
o восьмеричное целое Произвольное число цифр (0-7), возможно, начинающихся с + или -.
x Шестнадцатеричное целое Произвольное число цифр (0-F), возможно, начинающихся с + или — и префикса 0x или 0X.
f, e, g Число с плавающей точкой Число, состоящее из набора цифр 0-9, возможно с десятичным разделителем (точкой). Возможно также представление в экспоненциальной форме. C99 позволяет также вводить число в шестнадцатеричном формате.
a
c Символ Если ширина не передана, то считывает один символ. Если ширина передана, то считывает нужное количество символов и размещает их в массиве БЕЗ терминального символа на конце.
s Строка Считывает все не пробельные символы. Если указана ширина, то не более n символов. Ставит на место n+1 символа терминальный.
p Адрес указателя Последовательность символов, трактуемая как адрес указателя. Формат зависит от реализации, но совпадает с тем, как выводит printf с ключом p
[символы] Множество символов Считывает только те символы, которые записаны в квадратных скобках, С99
[^символы] Множество символов Считывает только те символы, которые не указаны в квадратных скобках, С99
n Ничего не считывает Сохраняет число уже считанных символов по указанному адресу

Как и в printf, ширина, заданная символом * ожидает аргумента, который будт задавать ширину. Флаг длина совпадает с таким флагом функции printf.

Кроме функций scanf и printf есть ещё ряд функций, которые позволяют получать вводимые данные

int getch() [aka _getch(), getchar()] — возвращает введённый символ, при этом не выводит его на консоль.

char * fgets ( char * str, int num, FILE * stream ) — функция позволяет считывать строку с пробельными символами. Несмотря на то, что она работает с файлом, можно с её помощью считывать и из стандартного потока ввода. Её преимущество относительно gets в том, что она позволяет указать максимальный размер считываемой строки и заканчивает строку терминальным символом.

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

Непечатные символы

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

Fscanf форматный ввод из файла

БлогNot. Как всё-таки победить fscanf при чтении чисел из текстового файла :)

Как всё-таки победить fscanf при чтении чисел из текстового файла :)

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

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

Проще всего, видимо, сделать это за 2 прохода по файлу — на первом проходе подсчитать, сколько в файле чисел, затем выделить память под динамический массив, встать на начало файла и повторить чтение уже известного объёма данных.

Задачи «подсчитать в текстовом файле количество значений заданного формата» и «переписать значения, заданные в нужном формате, в массив» похожи, так что функция для них будет одна и та же —

int read_float_numbers (FILE *fp, bool onlycount, float *a);
fp — дескриптор открытого файла;
onlycount — если true, только считает количество чисел, иначе пишет их в a;
a — место, куда писать (память должна быть выделена).

Тестировать такой код имеет смысл на всех типовых ситуациях:

конец последнего числа есть последний символ файла:

после последнего числа есть разделители (перевод строки, пробелы):

файл заканчивается «мусором» без перевода строки:

файл заканчивается «мусором» с переводом строки:

в файле есть значения и «мусор» вперемешку:

файл вообще пуст или состоит только из мусора:

файл содержит «числа» типа 1e1000 или 9999999999999999999999999999:

Вот примерно такой код выходит (проверен в Visual Studio):

Самый непростой момент — как раз со «слишком большими числами». Например, 1e1000 останется для fscanf «числом», хотя и вызовет в Studio «превращение» числа в значение 1.#INFOOO . Нам просто повезло, что для .NET оно «меньше» FLT_MAX (см. комментарий, изначально в коде стоял знак «не равно»).

Как ни странно, сам по себе C/C++ встроенных кроссплатформенных средств обнаружения такого переполнения не имеет. Единственный реально надёжный способ — прочитать все данные в строковый буфер и потом его проанализровать. А не scanf’ы :)

P.S. Предполагается, что текстовый файл с именем data.txt находится в «текущей папке текущего проекта», например, если у Вас Windows 7, имя решения и проекта = Console, имя юзера = ПК, версия Studio = 2010, а конфигурация решения = Debug, это будет

21.10.2020, 15:52; рейтинг: 4091

Понравилась статья? Поделиться с друзьями:
Кодинг, CSS и SQL