Основы программирования на c/c++ Лекция 3: Файлы.
План
Файл как основа целостности
Базовые операции с файлами
Базовые операции с файлами
Базовые операции с файлами
Текстовые файлы
Бинарные файлы
Выбор режима: текст vs бинарный
Потоки данных (Streams):
<fstream>: кроссплатформенный стандарт
Пример
Пример
Пример 2: Чтение из текстового файла построчно
Пример 2: Чтение из текстового файла построчно
Особенности бинарного режима
Особенности бинарного режима
Особенности бинарного режима
Чтение в бинарном режиме
Чтение в бинарном режиме
Позиционирование в файле: зачем нужно?
Методы позиционирования в потоках
Точки отсчета для позиционирования 
Пример : Определение размера файла через позиционирование
 Процедурный подход: <stdio.h>
Указатель на FILE и функция fopen
Строки режимов открытия файла в fopen
Пример: Открытие и проверка в fopen
Функции чтения и записи
Пример: Форматированная запись
 Пример: Бинарная запись структуры с fwrite
Позиционирование: fseek и ftell
Пример
WinAPI: Мощь и контроль операционной системы
Базовые концепции: дескрипторы и функции
Функция CreateFile
Функция CreateFile
Параметры CreateFile
ДЗ 3, pr7
Контрольная работа 3
225.00K
Категория: ПрограммированиеПрограммирование

op3

1. Основы программирования на c/c++ Лекция 3: Файлы.

oopCpp@yandex.ru
1

2. План

Введение и фундаментальные понятия: Для чего нужны файлы,
базовые операции, текстовый vs бинарный режим.
Часть 1: Объектно-ориентированный подход — <fstream>: Краткий
разбор классов, методов, примеры чтения, записи, позиционирования.
Часть 2: Процедурный подход — <stdio.h> (fopen): Изучение
функций, работа с указателями на файл, тонкости использования.
Часть 3: Низкоуровневый подход — WinAPI: Максимальный
контроль и производительность средствами операционной системы.
Сравнительный анализ и заключение: Детальное сравнение трех
подходов, рекомендации по применению, выводы
2

3. Файл как основа целостности

Файл представляет собой именованную область данных на
физическом носителе информации, таком как жесткий диск (HDD) или
твердотельный накопитель (SSD).
Эта область хранит информацию в виде последовательности байтов,
которая сохраняется после завершения работы программы, обеспечивая
тем самым постоянство данных (персистентность).
Каждый файл обладает набором атрибутов, включая имя, расширение,
путь к местоположению в файловой системе, размер, а также даты
создания и изменения. Понимание природы файла является первым
шагом к эффективной работе с данными на диске.
3

4. Базовые операции с файлами

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

5. Базовые операции с файлами

Чтение и запись:
После успешного открытия программа может читать данные из файла
в оперативную память или записывать данные из памяти в файл.
Эти операции передачи данных между разными уровнями хранения —
быстрой, но энергозависимой оперативной памятью и медленной, но
постоянной дисковой подсистемой.
Эффективность выполнения этих операций критически важна для
производительности приложения, особенно при работе с большими
объемами информации, и сильно зависит от выбранного метода работы
с файлом.
5

6. Базовые операции с файлами

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

7. Текстовые файлы

Текстовые файлы предназначены для хранения информации в форме,
удобной для чтения человеком. Данные в них интерпретируются как
последовательности символов, организованные в строки, которые часто
разделяются символами перевода строки.
Ключевой особенностью текстовых файлов является использование
кодировок (например, ASCII, UTF-8, Windows-1251), которые определяют
соответствие между числовыми кодами и символами.
Примеры текстовых файлов включают исходный код программ (*.cpp,
*.h), файлы конфигураций (*.ini, *.json), логи и обычные документы (*.txt).
7

8. Бинарные файлы

Бинарные файлы хранят данные в том виде, в каком они
представлены в оперативной памяти программы, без какого-либо
преобразования в символьные представления. Они содержат
последовательность байтов, и их структура понятна только программе,
которая их создала.
Бинарные файлы используются для эффективного хранения любых
данных, не являющихся чисто текстовыми: исполняемые программы
(*.exe, *.dll), изображения (*.jpg, *.png), аудиофайлы (*.mp3), а также
любые специализированные форматы данных приложений (*.dat). Их
главное преимущество — скорость чтения/записи и компактность.
8

9. Выбор режима: текст vs бинарный

Выбор между текстовым и бинарным режимом является одним из
самых важных проектных решений при работе с файлами.
Текстовый режим следует использовать, когда данные должны быть
удобочитаемы или редактируемы человеком, либо когда они должны
быть переносимы между системами с разными архитектурами.
Бинарный режим незаменим для максимальной производительности,
компактности хранения и работы со сложными структурами данных.
Ошибка в выборе режима (например, открытие бинарного файла в
текстовом режиме) может привести к порче данных из-за нежелательных
преобразований символов.
9

10. Потоки данных (Streams):

Поток данных (stream) — это фундаментальная абстракция,
представляющая собой последовательность байтов, перемещающуюся
от источника к приемнику. Эта абстракция позволяет унифицировать
интерфейс для работы с различными устройствами ввода-вывода:
файлами, стандартной консолью, строками в памяти и сетевыми
соединениями.
Основное разделение происходит на входные потоки, используемые
для чтения данных, и выходные потоки, используемые для их записи.
Именно на этой абстракции построена вся современная система вводавывода .
10

11. <fstream>: кроссплатформенный стандарт

<fstream>: кроссплатформенный
стандарт
Заголовочный файл <fstream> является частью стандартной
библиотеки (stl) и предоставляет набор классов для объектноориентированной работы с файлами.
Эти классы инкапсулируют низкоуровневые системные вызовы
операционной системы, предоставляя программисту удобный,
безопасный и переносимый интерфейс.
Главным преимуществом данного подхода является его
кроссплатформенность: код, написанный с использованием <fstream>,
будет успешно компилироваться и работать на любой операционной
системе (Windows, Linux, macOS) без внесения изменений, что
значительно упрощает разработку и поддержку.
11

12. Пример

#include <fstream>
#include <string>
int main() {
// Создаем объект выходного файлового потока
// и сразу открываем файл "log.txt".
// По умолчанию применяются флаги std::ios::out | std::ios::trunc.
std::ofstream
file ("log.txt");
// Проверяем, что файл действительно открылся и готов к работе.
if ( ! file.is_open()) {
// Выводим сообщение об ошибке в стандартный поток ошибок.
std::cerr << "Ошибка! Не удалось открыть файл!" << std::endl;
return 1; // Завершаем программу с кодом ошибки.
}
12

13. Пример

// … продолжение
// Используем оператор << для записи данных в файл.
file << "Hello, File" << std::endl;
int value = 17;
file << value << std::endl;
// Явно закрываем файл.
// это хорошая практика для немедленного сохранения данных.
file.close();
return 0;
}
13

14. Пример 2: Чтение из текстового файла построчно

#include <fstream>
#include <iostream>
#include <string>
int main() {
std::ifstream file("log.txt");
std::string line; // Переменная для хранения считанной строки.
// Проверяем статус открытия файла с помощью приведения к bool.
if (file) {
// Функция std::getline читает данные из потока 'file' до символа
// перевода строки и помещает результат в строку 'line'.
// Цикл продолжается, пока getline успешно считывает данные.
while (std::getline(file, line)) {
// Выводим считанную строку на стандартный вывод (консоль).
std::cout << "Прочитано: " << line << std::endl;
}
14

15. Пример 2: Чтение из текстового файла построчно

// … продолжение
} else {
std::cerr << "Ошибка открытия файла!" << std::endl;
}
// Закрытие файла произойдет автоматически при разрушении объекта
'file'.
return 0;
}
15

16. Особенности бинарного режима

Проблема текстового режима в Windows:
В текстовом режиме происходит невидимое для программиста
преобразование символов. В частности, при записи символа перевода строки
\n (LF, код 10) он преобразуется в последовательность \r\n (CRLF, коды 13 10).
При чтении последовательность \r\n, наоборот, преобразуется обратно в \n.
Это исторически сложившееся поведение для совместимости с разными ОС.
Однако для не-текстовых данных (картинки, EXE-файлы) такое
преобразование катастрофически портит данные.
16

17. Особенности бинарного режима

#include <fstream>
// Простая структура, представляющая данные для сохранения.
struct Person {
char name[50]; // Фиксированный размер поля.
int age;
double height;
};
int main() {
Person p = {“Ivan Ivanov", 20, 185.5}; // Инициализируем структуру.
// Ключевой момент: используем флаг std::ios::binary.
std::ofstream file("person.dat", std::ios::binary);
17

18. Особенности бинарного режима

// … продолжение
if (file) {
// Метод write() записывает блок сырых байтов.
// Первый аргумент: указатель на начало блок памяти (адрес структуры p).
// Второй аргумент: количество байтов для записи (размер структуры).
file.write( reinterpret_cast<char*>(&p), sizeof (Person) );
}
// Важное предупреждение: такой способ не переносим между разными
// компиляторами и платформами
// из-за различий в выравнивании и размерах типов.
return 0;
}
18

19. Чтение в бинарном режиме

#include <fstream>
#include <iostream>
struct Person {
char name[50];
int age;
double height;
};
int main() {
Person p; // Создаем объект, в который будем считывать данные.
std::ifstream file("person.dat", std::ios::binary); // в бинарном режиме.
if (file) {
// read читает блок байтов из файла и помещает его по указанному адресу.
file.read(reinterpret_cast<char*>(&p), sizeof(Person));
19

20. Чтение в бинарном режиме

// … продолжение
// После чтения мы можем использовать данные из структуры.
std::cout << "Name: " << p.name << "\nAge: " << p.age
<< "\nHeight: " << p.height << std::endl;
} else {
std::cerr << "Файл не найден или не может быть открыт!" << std::endl;
}
return 0;
}
20

21. Позиционирование в файле: зачем нужно?

Часто возникает необходимость читать или записывать данные не в начало
или конец файла, а в некоторое произвольное место. Например, обновить
запись о конкретном пользователе в середине большого файла базы данных.
Для этого необходимы функции, позволяющие управлять текущей позицией
— указателем, который определяет, где будет произведена следующая
операция чтения или записи. Это называется прямым или произвольным
доступом (random access).
21

22. Методы позиционирования в потоках

Для входных потоков (ifstream):
tellg() — (tell get) возвращает текущую позицию указателя чтения.
seekg(pos) — (seek get) устанавливает указатель чтения на абсолютную
позицию pos.
seekg(offset, origin) — устанавливает указатель чтения относительно точки
origin на смещение offset.
Для выходных потоков (ofstream):
tellp() — (tell put) возвращает текущую позицию указателя записи.
seekp(pos) — (seek put) устанавливает указатель записи на абсолютную
позицию pos.
seekp(offset, origin) — устанавливает указатель записи относительно точки
origin на смещение offset.
22

23. Точки отсчета для позиционирования 

Точки отсчета для
позиционирования
• std::ios::beg — начало файла.
• std::ios::end — конец файла.
• std::ios::cur — текущая позиция в файле.
23

24. Пример : Определение размера файла через позиционирование

#include <fstream>
#include <iostream>
int main() {
std::ifstream file("large_data.bin", std::ios::binary);
if (file) {
// Перемещаем указатель чтения на 0 байт от конца файла.
file.seekg(0, std::ios::end);
// tellg() возвращает текущую позицию, теперь она равна размеру файла.
std::streamsize fileSize = file.tellg();
// Возвращаем указатель в начало для последующего чтения.
file.seekg(0, std::ios::beg);
std::cout << "Размер файла: " << fileSize << " байт." << std::endl;
}
return 0;
}
24

25.  Процедурный подход: <stdio.h>

Процедурный подход: <stdio.h>
Заголовочный файл <cstdio> (унаследованный от C как stdio.h)
предоставляет набор процедурных функций для работы с вводом-выводом,
включая файловый.
Этот подход старше и более низкоуровневый, чем <fstream>, но все еще
широко используется, особенно в legacy-коде и в случаях, когда требуется
минимализм и прямой контроль.
В отличие от объектно-ориентированного подхода, здесь работа ведется не с
объектами-потоками, а с указателями на структуру FILE и набором
независимых функций, которые принимают этот указатель в качестве
аргумента.
25

26. Указатель на FILE и функция fopen

Указатель на FILE и
функция fopen
FILE*: Это указатель на структуру, которая содержит всю необходимую
информацию об открытом файле: его дескриптор, текущую позицию, буферы,
флаги ошибок и т.д. Программист работает исключительно с этим указателем,
не имея прямого доступа к содержимому структуры.
fopen: Функция для открытия файла. Принимает два аргумента: путь к файлу
(в виде строки типа const char*) и строку режима (например, "r", "w"). В случае
успеха возвращает переменную типа FILE*, в случае неудачи — NULL.
26

27. Строки режимов открытия файла в fopen

Строки режимов открытия файла
в fopen
"r" — (read) открыть для чтения (файл должен существовать).
"w" — (write) открыть для записи (создает файл или перезаписывает
существующий).
"a" — (append) открыть для добавления в конец (создает файл, если не
существует).
"r+" — открыть для чтения и записи (файл должен существовать).
"w+" — открыть для чтения и записи (создает или перезаписывает файл).
"a+" — открыть для чтения и добавления (создает файл, если не существует).
Добавление "b" — открыть в бинарном режиме (например, "rb", "w+b").
27

28. Пример: Открытие и проверка в fopen

Пример: Открытие и проверка
в fopen
#include <stdio.h> // Для FILE, fopen, fclose и т.д.
#include <iostream>
int main() {
// Открываем файл для записи в текстовом режиме.
FILE* file = fopen("data_c.txt", "wt");
// Критически важно проверить результат открытия!
if (file == nullptr) { // или if ( ! file)
std::perror("Ошибка открытия файла"); // Выводит сообщение об ошибке.
return 1;
}
// Файл открыт успешно, можно работать...
fclose(file); // ОБЯЗАТЕЛЬНО закрываем файл!
return 0; }
28

29. Функции чтения и записи

Для форматированного текстового ввода/вывода:
fprintf(file, format, ...) — аналог printf, но вывод в файл.
fscanf(file, format, ...) — аналог scanf, но ввод из файла.
Для бинарного и построчного ввода/вывода:
fread(ptr, size, count, file) — читает count элементов размером size байт в
буфер ptr.
fwrite(ptr, size, count, file) — пишет count элементов из буфера ptr в файл.
fgets(str, n, file) — читает строку (до n-1 символов) в массив str.
fputs(str, file) — пишет строку в файл.
29

30. Пример: Форматированная запись

#include <stdio.h>
int main() {
FILE* file = fopen("log_c.txt", "w");
if (file) {
const char* message = "Hello from fprintf!";
int number = 42;
// Записываем в файл отформатированную строку.
fprintf ( file, "%s\nNumber: %d\n", message, number );
fclose(file);
}
return 0; }
// Hello from fprintf!
// Number: 42
30

31.  Пример: Бинарная запись структуры с fwrite

Пример: Бинарная запись
структуры с fwrite
#include <stdio.h>
struct Person {
char name[50];
int age;
double height;
};
int main() {
Person p = {“Ivan Sidorow", 18, 170.0};
FILE* file = fopen("person_c.dat", "wb"); // "wb" - write binary
if (file) {
size_t elements_written = fwrite( &p, sizeof(Person), 1, file);
if (elements_written != 1) { /* Обработка ошибки записи*/
fclose(file);
}
return 0; }
31
}

32. Позиционирование: fseek и ftell

Позиционирование: fseek и ftell
int fseek (FILE* stream, long offset, int origin) — устанавливает указатель
позиции в файле.
позиции: SEEK_SET (начало),
SEEK_CUR (текущая позиция),
SEEK_END (конец).
long ftell ( FILE* stream) — возвращает текущую позицию в файле.
32

33. Пример

FILE* file = fopen("data.bin", "rb");
if (file) {
fseek(file, 0, SEEK_END); // Переместиться в конец файла.
long fileSize = ftell (file); // Узнать размер файла.
rewind(file); // Макрос, эквивалентный fseek(file, 0, SEEK_SET); - в начало.
fclose(file);
}
33

34. WinAPI: Мощь и контроль операционной системы

WinAPI (Windows Application Programming Interface) — это обширный набор
низкоуровневых функций, структур и констант, предоставляемых операционной
системой Microsoft Windows для взаимодействия с ее ядром и компонентами.
В отличие от стандартных библиотек C и C++, WinAPI предоставляет прямой,
ничем не опосредованный доступ к функционалу ОС, что дает программисту
беспрецедентный уровень контроля над всеми аспектами работы с системой,
включая файловый ввод-вывод, управление памятью, процессами, потоками и
графическим интерфейсом.
Работа с файлами через WinAPI является фундаментом, на котором
построены все другие, более высокоуровневые методы в среде Windows.
34

35. Базовые концепции: дескрипторы и функции

Дескриптор (HANDLE): Ключевое понятие в WinAPI. Дескриптор — это не
просто число, а абстрактная ссылка на внутренний объект ядра Windows (в
данном случае — на открытый файл). После успешного открытия
функция CreateFile возвращает этот дескриптор, который затем необходимо
передавать во все последующие функции (ReadFile, WriteFile, CloseHandle) для
идентификации файла, с которым производится операция.
Тип HANDLE инкапсулирует этот идентификатор, обеспечивая
типобезопасность.
Функции: WinAPI представляет собой именно процедурный интерфейс,
состоящий из множества функций
(например, CreateFileW, ReadFile, SetFilePointer), а не классов с методами.
35

36. Функция CreateFile

Функция CreateFile
#include <windows.h> // Главный заголовочный файл для WinAPI
int main() {
// Внимание: Используется широкий символ (wchar_t)
// и префикс L для поддержки Unicode.
HANDLE hFile = CreateFileW( L"C:\\path\\to\\file.txt", // Путь к файлу (Unicode)
GENERIC_READ | GENERIC_WRITE, // Желаемый доступ: чтение и запись
FILE_SHARE_READ,
NULL,
// Режим совместного доступа: другие могут читать
// Атрибуты безопасности (по умолчанию)
OPEN_ALWAYS, // Стратегия создания: открыть, если есть, иначе создать
FILE_ATTRIBUTE_NORMAL,
NULL
);
// Флаги атрибутов файла (обычный файл)
// не используется
36

37. Функция CreateFile

Функция CreateFile
// продолжение
if (hFile == INVALID_HANDLE_VALUE) {
// Ошибка! Анализируем код с помощью GetLastError()
DWORD err = GetLastError();
return 1;
}
// Файл успешно открыт, работаем с hFile...
CloseHandle(hFile); // ОБЯЗАТЕЛЬНО закрываем описатель !
return 0;
}
37

38. Параметры CreateFile

Параметры CreateFile
Параметры доступа (dwDesiredAccess):
GENERIC_READ: Запрос права на чтение.
GENERIC_WRITE: Запрос права на запись.
Параметры создания (dwCreationDisposition):
CREATE_NEW: Создать новый; ошибка, если существует.
CREATE_ALWAYS: Создать новый; перезаписать существующий.
OPEN_EXISTING: Открыть существующий; ошибка, если не существует.
OPEN_ALWAYS: Открыть существующий; создать, если не существует.
TRUNCATE_EXISTING: Открыть и обнулить длину; ошибка, если не существует.
Флаги и атрибуты (dwFlagsAndAttributes):
FILE_ATTRIBUTE_NORMAL: Самый обычный файл.
FILE_FLAG_SEQUENTIAL_SCAN: Оптимизация для последовательного доступ
38

39. ДЗ 3, pr7

Использовать каждый из трех способов (fstream, fopen, WinAPI) для
работы с файлами:
1. CreateFile – создать новый файл через WinAPI
Если файл уже существует, спросить пользователя о его
перезаписи.
2. Add: через функции с-библиотеки
Добавить новую произвольную запись (предложение) в конец
файла.
Затем еще одну, в начало файла.
Затем третье предложение – снова в конец файла.
Данные вводятся пользователем с клавиатуры через консоль.
3. Read: прочитать файл через fstream
4. Вывести текст на консоль.
Крайний срок присылки: воскресенье
39

40. Контрольная работа 3

Напишите на листке свою фамилию имя и группу.
Объясните, какие из следующих строк кода скомпилируются, а какие
— нет, и почему.
int x = 10;
const int y = 20;
int& r1 = x; // 1 : ответ и объяснение
int& r2 = y; // 2 : …
int& r3 = 30; // 3
const int& cr1 = x; // 4
const int& cr2 = y; // 5
const int& cr3 = 40; // 6
40
English     Русский Правила