Cstdio c что это
БлогNot. Лекции по C/C++: работа с файлами (stdio.h)
Лекции по C/C++: работа с файлами (stdio.h)
В лекции рассмотрен классический способ работы с файлами в C/C++, основанный на библиотеке stdio.h (она же cstdio ) и доступе к данным через структуру FILE . Альтернативный современный механизм работы с файлами в языке C++ на основе потоков и библиотек <fstream> , <ifstream> , <ofstream> будет изучен в следующей лекции.
Базовые функции для работы с файлами описаны в библиотеке stdio.h . Вся работа с файлом выполняется через файловую переменную — указатель на структуру типа FILE , определённую в стандартной библиотеке:
Открыть файл можно функцией fopen , имеющей 2 параметра:
Параметр имя_файла может содержать относительный или абсолютный путь к открываемому файлу:
1) «data.txt» — открывается файл data.txt из текущей папки
Важно: при запуске exe-файла «текущая папка» – та, где он находится; при отладке в IDE папка может быть иной, например, в Visual Studio при открытом консольном решении с именем Console файл следует разместить в папке Console/Console , а при запуске исполняемого файла не из IDE – в папке Console/Debug .
2) «f:\\my.dat» — открывается файл my.dat из головной папки диска f:
3) имя файла запрашивается у пользователя:
Параметр режим_доступа определяет, какие действия будут разрешены с открываемым файлом, примеры его возможных значений:
1) «rt» — открываем для чтения текстовый файл;
2) «r+b» — открываем для произвольного доступа (чтение и запись) бинарный файл;
3) «at» – открываем текстовый файл для добавления данных в конец файла;
4) «w» — открываем файл для записи без указания того, текстовый он или бинарный.
Фактически, указание «r» или «t» не накладывает каких-либо ограничений на методы, которые мы будем применять для чтения или записи данных.
После открытия файла следует обязательно проверить, удалась ли эта операция. Для этого есть 2 основных подхода:
1) стандартный обработчик ferror (см. пособиe, п.8.7);
2) сравнить указатель, который вернула fopen , с константой NULL ( nullptr ) из стандартной библиотеки:
Пример. Приложение проверяет, удалось ли открыть файл из текущей папки, имя файла запрашивается у пользователя (Visual Studio)
Важно! Функции, возвращающие указатель, в том числе, fopen , считаются небезопасными в ряде новых компиляторов, например, Visual Studio 2015. Если их использование приводит не просто к предупреждению, а к генерации ошибок, есть 2 основных способа решения проблемы:
1) в соответствии с рекомендациями компилятора, заменить старые названия функций на их безопасные версии, например, strcpy на strcpy_s и fopen на fopen_s . При этом может измениться и способ вызова функций, например,
2) в начало файла (до всех #include ) включить директиву
Если используется предкомпиляция, то можно определить этот макрос в заголовочном файле stdafx.h .
Выбор способа чтения или записи данных зависит от того, какой должна быть структура файла.
- fscanf — для чтения
- fprintf — для записи
Первым параметром этих функций указывается файловая переменная, в остальном работа совпадает со стандартными scanf и printf .
Пример. Файл text.txt в текущей папке приложения имеет следующий вид:
Прочитаем его как последовательность вещественных чисел.
1. Функции семейства scanf возвращают целое число — количество значений, которые успешно прочитаны в соответствии с указанным форматом. В реальных приложениях эту величину следует проверять в коде:
2. На «восприятие» программой данных может влиять установленная в приложении локаль. Например, если до показанного кода выполнен оператор результат работы кода может измениться (для русской локали разделителем целой и дробной части числа является запятая, а не точка).
3. Очередное чтение данных изменяет внутренний файловый указатель. Этот указатель в любой момент времени, пока файл открыт, показывает на следующее значение, которое будет прочитано. Благодаря этому наш код с «бесконечным» while не зациклился.
4. Код показывает, как читать из файла заранее неизвестное количество значений – это позволяет сделать стандартная функция feof (проверка, достигнут ли конец файла; вернёт не 0, если прочитано всё).
5. Распространённый в примерах из Сети код вида
в ряде компиляторов может породить неточности при интерпретации данных. Например, этот код может прочитать как последнее значение завершающий перевод строки в файле, благодаря чему последнее прочитанное значение «удвоится».
В качестве примера форматной записи в файл сохраним массив a из 10 целочисленных значений в файле с именем result.txt по 5 элементов в строке:
Важно! Ввод/вывод функциями библиотеки stdio.h буферизован, то есть, данные «пропускаются» через область памяти заданного размера, обмен данными происходит не отдельными байтами, а «порциями». Поэтому перед чтением данных желательно очищать буфер от возможных «остатков» предыдущего чтения методом fflush , а после записи данных следует обязательно закрывать файл методом fclose , иначе данные могут быть потеряны. Заметим, что консольный ввод/вывод «обычными» методами scanf и printf также буферизован.
- fgetc и fputc — для посимвольного чтения и посимвольной записи данных;
- fgets и fputs — для чтения и записи строк с указанным максимальным размером.
Как и в случае с функциями для чтения форматированных данных, у всех этих методов имеются аналоги для работы со стандартным вводом/выводом.
Пример. Читая файл, определить длину каждой строки в символах. Для решения задачи воспользуемся тем фактом, что строки завершаются символом «перевод строки» ( ‘\n’ ). Предполагается, что файл уже открыт для чтения.
Важно! Из-за особенностей реализации fgetc , без последней проверки за телом цикла код мог «не обратить внимания», например, на последнюю строку файла, состоящую только из пробелов и не завершающуюся переводом строки.
Пример. Читаем построчно файл с известной максимальной длиной строки. Предполагается, что файл уже открыт для чтения.
Важно! Без дополнительной обработки прочитанные из файла строки при выводе будут содержать лишние пустые строки между строками данных. Это происходит потому, что функция fgets читает строку файла вместе с символом перевода строки (точней, под Windows — с парой символов \r\n , интерпретируемых как один), а puts добавляет к выводимой строке ещё один перевод строки.
- void *buffer — нетипизированный указатель на место хранения данных;
- size_t (unsigned) size — размер элемента данных в байтах.
- size_t count — максимальное количество элементов, которые требуется прочитать (записать);
- FILE *stream — указатель на структуру FILE
Пример. Целочисленный массив a запишем в двоичный файл.
Учитывая, что данные массива хранятся в последовательно идущих адресах памяти, цикл for для записи мы могли заменить одним оператором:
Подход к чтению данных с помощью fread аналогичен. Например, если файл уже открыт для чтения в режиме «rb»:
- функции fgetpos и ftell позволяют выполнить чтение текущей позиции указателя в файле;
- функции fseek и fsetpos позволяют осуществить переход к нужной позиции в файле.
Пример. Определить размер файла в байтах, предположим, что файл уже открыт в режиме чтения или произвольного доступа.
Материал для чтения из пособия: пп. 8.6-8.11. Обратите внимание на таблицы с описанными прототипами функций ввода/вывода.
Рекомендуемые задачи: базовое задание включает две задачи, первая из которых предполагает обработку файла как текстовых данных, вторая – как бинарных. В качестве дополнительной третьей задачи может быть предложена реализация одной из задач 1, 2, содержащая консольный интерфейс и меню.
В чем отличие cstdio от stdio.h
Вероятно заголовок моего вопроса звучит глупо. Но, у меня вопрос следующего рода: в данном видео (27:15-27:50) человек решает задачу и говорит, что большая часть времени занимает сугубо ввод и вывод. А далее вместо #include <cstdio> пишет #include <stdio.h> , и меняет тип выводимой переменной с %lld на %I64 и это ускоряет программу аж в 2 раза. Почему данные действия ускорили программу?
Программу компилировали MinGW. Он может использовать две разные реализации printf :
- от майкрософта
- свою самодельную (дальше буду называть ее "ANSI")
Похоже, что первый вариант быстрее. Но он не умеет как минимум в %lld (поэтому на видео используют нестандартный %I64d ) — поэтому и сделали альтернативную версию.
Экспериментально выяснил, что выбор между ними происходит так:
| Язык | Хедер | Вариант |
|---|---|---|
| C | <stdio.h> | -std=c90 и старее -> MS -std=c99 и новее -> ANSI |
| C++ | <stdio.h> | -std=c++98 -> MS -std=c++11 и новее -> ANSI |
| C++ | <cstdio> | всегда ANSI |
Проверял на MSYS2. Без явного указания стандарта, у меня всегда выбирался ANSI — потому что дефолтный стандарт достаточно новый.
Можно переопределить выбор варианта, используя -D__USE_MINGW_ANSI_STDIO=0 и 1 (MS и ANSI соответственно), или #define __USE_MINGW_ANSI_STDIO 0 и 1 (до инклудов).
Проверить, какой вариант используется, можно #if __USE_MINGW_ANSI_STDIO == 1 .
Поскольку дефолтное значение зависит от стандарта, выбирать его через разные варианты хедера (как на видео) — выглядит ненадежно. Если для вас это важно, лучше явно задавать __USE_MINGW_ANSI_STDIO .
Stdio.h
stdio.h (от англ. standard input/output header — стандартный заголовочный файл ввода/вывода) заголовочный файл стандартной библиотеки языка Си, содержащий определения макросов, константы и объявления функций и типов, используемых для различных операций стандартного ввода и вывода. Функциональность унаследована от «портативного пакета ввода/вывода» («portable I/O package»), написанного Майком Леском из Bell Labs в начале 1970-х. [1] C++ ради совместимости, также использует stdio.h наряду со схожим по функциональности заголовочным файлом cstdio.
Функции, объявленные в stdio.h , являются весьма популярными благодаря тому, что являясь частью Стандартной библиотеки языка Си, они гарантируют работу на любой платформе, поддерживающей Си. Приложения на отдельных платформах могут, тем не менее, иметь причины для использования функций ввода/вывода самой платформы вместо функций stdio.h .
Содержание
Пример использования
Все функции в языке Си (и его многочисленных разновидностей) объявляются в заголовочных файлах. Таким образом, программистам приходится подключать файл stdio.h к исходному коду, чтобы использовать функции, объявленные в нем.
Программа, приведенная выше, считывает всю входящую информацию из стандартного ввода и выводит ее побайтно на стандартный вывод, добавляя символ перевода строки в конец вывода.
Функции-члены
Функции, объявленные в stdio.h в общем случае могут быть разделены на две категории: функции для операций с файлами и функции для операций ввода-вывода.