Файловый ввод-вывод
Файловый ввод-вывод
Программа на С взаимодействует с диском через потоки (streams) из <stdio.h> — абстракцию «последовательность байт с буфером». Консоль — те же потоки: stdin (ввод), stdout (вывод), stderr (ошибки).
Файл на диске открывают функцией fopen; она возвращает указатель FILE * — дескриптор потока в памяти программы. Закрытие fclose сбрасывает буферы и освобождает ресурс ОС.
Подробные сигнатуры — в справочнике. Ошибки — по идиомам.
Термины
| Термин | Объяснение |
|---|---|
FILE * | объект потока; не путать с «файлом» на диске |
| Текстовый режим | возможны преобразования \n ↔ \r\n (Windows) |
Бинарный режим ("rb") | байты как есть |
| Буферизация | библиотека копит данные пачками для скорости |
EOF | конец файла при чтении |
Открытие и закрытие
#include <stdio.h>
FILE *f = fopen("data.txt", "r");
if (f == NULL) {
perror("data.txt");
return 1;
}
/* ... */
fclose(f);
Режимы второго аргумента:
| Режим | Назначение |
|---|---|
"r" | чтение, файл должен существовать |
"w" | запись, создать или обнулить |
"a" | дозапись в конец |
"rb", "wb" | бинарный (важно на Windows) |
"r+", "w+" | чтение и запись |
fopen возвращает NULL при ошибке; детали — errno и perror/strerror.
Разбор строки:
FILE *f = fopen("data.txt", "r");
"data.txt"— путь относительно текущей рабочей директории процесса (откуда запустили программу), если не указан полный путь;"r"— только чтение; файл должен существовать;f— либо валидный поток, либоNULL.
Текстовый ввод-вывод
Построчное чтение:
char line[256];
while (fgets(line, sizeof line, f) != NULL) {
/* line может содержать '\n' */
}
fgets ограничивает длину — защита от переполнения буфера. Не путать с небезопасным gets (удалён из стандарта).
Возвращаемое значение fgets:
- указатель
line— строка прочитана (может включать\nв конце); NULL— конец файла или ошибка; отличить:feof(f)иferror(f).
if (fgets(line, sizeof line, f) == NULL) {
if (ferror(f))
perror("read");
/* feof — нормальный конец файла */
}
Форматированный вывод:
fprintf(f, "id=%d name=%s\n", id, name);
Форматированный ввод fscanf удобен для прототипов; для ненадёжных внешних файлов лучше парсить вручную или по шаблону с проверкой каждого поля.
Запись символа/строки: fputc, fputs.
Бинарный ввод-вывод
В бинарном режиме байты передаются без преобразования перевода строк. Блоковые операции:
size_t n = fread(buffer, 1, sizeof buffer, f);
if (n == 0 && ferror(f)) { /* ошибка чтения */ }
size_t w = fwrite(data, elem_size, count, f);
fread/fwrite возвращают число успешно обработанных элементов (не байт, если elem_size > 1). Всегда сравнивать с ожидаемым count.
Пример: прочитать ровно 100 байт в буфер:
size_t got = fread(buf, 1, 100, f);
if (got != 100) {
if (ferror(f)) { /* ошибка диска */ }
else if (feof(f)) { /* файл короче 100 байт */ }
}
На Windows для бинарных форматов (картинки, .db) режим "rb" / "wb" обязателен — иначе байт 0x1A может интерпретироваться особым образом в текстовом режиме.
Буферизация
Библиотека буферизует поток для производительности. Режимы: полная буферизация, построчная (терминал), без буфера.
fflush(f); /* сбросить буфер записи на диск/ОС */
setvbuf(f, ...); /* настроить при открытии */
При аварийном завершении без fclose/fflush часть данных может остаться в буфере. Для критичных записей — fflush после важных блоков или открытие с немедленной записью.
Позиционирование
fseek(f, 0, SEEK_END);
long size = ftell(f);
rewind(f);
fseek(f, offset, SEEK_SET);
Для файлов больше 2 ГБ на 32-битных платформах используют fseeko/ftello (POSIX). Случайный доступ удобен для индексированных форматов и обновления записи фиксированной длины.
Сериализация структур
Запись struct целиком через fwrite(&obj, sizeof obj, 1, f) не переносима между компиляторами, архитектурами и версиями из-за выравнивания и порядка байтов (endianness).
Переносимые варианты:
- поля по одному в известном порядке и фиксированных типах (
uint32_tиз<stdint.h>); - текстовые форматы (CSV, JSON через внешнюю библиотеку);
- явный бинарный протокол с магическим числом и версией заголовка.
Для учебных проектов на одной машине допустим «сырой» дамп, если файл не уходит на другую платформу.
Строки как потоки — sprintf / sscanf
Форматирование в память:
char buf[64];
snprintf(buf, sizeof buf, "value=%d", x);
int x;
sscanf(buf, "value=%d", &x);
snprintf ограничивает размер — предпочтительнее sprintf. Аналогично для разбора — проверять код возврата sscanf (число сопоставленных полей).
Связь с другими темами
- Логи и конфиги на диске — те же потоки; большие данные — встраиваемая БД.
- Дескрипторы низкого уровня (
open,read,write) — в системном программировании, поверх них часто строятstdio.
См. также: Память процесса, Преобразование в исполняемый файл.
См. также
Другие статьи этого же раздела в боковом меню (как на странице «О разделе»). История языка C - происхождение, ключевые идеи и влияние на развитие операционных систем и компиляторов. Язык С — это процедурный, компилируемый язык программирования, созданный в начале 1970-х годов Деннисом Ритчи в Bell Labs. Программирование на языке С требует понимания не только самого языка, но и всей совокупности программ, задействованных в процессе превращения исходного текста в исполняемый файл. Программа на языке С не выполняется напрямую процессором. Исходный текст проходит несколько этапов обработки, прежде чем превратится в машинный код, который может быть запущен операционной системой. Язык программирования С существует не как набор случайных правил, а как строго определённая спецификация, зафиксированная в международных стандартах. Как исполняемый файл на С раскладывается по областям памяти — код, данные, BSS, куча и стек — и что это даёт при отладке. Архитектура программ на C - организация модулей, процесс компиляции и взаимосвязь компонентов системы. Язык программирования С занимает особое место в истории и практике разработки программного обеспечения. Типизация, набор правил определения типа данных значений языка. Язык программирования С предоставляет механизм создания составных типов данных, позволяющих объединять разнородные элементы под единым именем. Этот механизм называется структурой. Как на С организовать функции, владение ресурсами, коды ошибок и очистку без исключений и сборщика мусора. Реализация ассоциативного массива на С — хеш-функция, коллизии, цепочки, открытая адресация и изменение размера.История языка С
Основы языка С
Инструментальная цепочка компиляции С
Преобразование исходного кода в исполняемый файл
Стандарты языка С
Память процесса и сегменты
Архитектура программ на С
Компиляторы и среды разработки для С
Типы данных в С
Структуры и объединения
Идиомы кода и обработка ошибок
Хеш-таблица на С