Работа с файловой системой в C++: от основ до продвинутых приемов
Работа с файловой системой — одна из фундаментальных задач при разработке на C++. Будь то сохранение настроек приложения, обработка логов или создание сложных систем управления данными — умение читать, записывать и манипулировать файлами и директориями необходимо каждому C++ разработчику. В этой статье мы рассмотрим как классические методы работы с файлами через fstream, так и современный подход с использованием библиотеки std::filesystem (C++17 и новее).
Основы работы с файлами: библиотека fstream
Библиотека fstream (<fstream>) предоставляет три основных класса для работы с файлами:
ifstream— для чтения из файла (input file stream);ofstream— для записи в файл (output file stream);fstream— для чтения и записи одновременно.
Чтение данных из файла
Рассмотрим простейший пример открытия и чтения текстового файла построчно:
#include <iostream>
#include <fstream>
#include <string>
int main() {
std::ifstream inputFile("data.txt");
if (!inputFile.is_open()) {
std::cerr << "Ошибка открытия файла!" << std::endl;
return 1;
}
std::string line;
while (std::getline(inputFile, line)) {
std::cout << line << std::endl;
}
inputFile.close();
return 0;
}
Важно всегда проверять успешность открытия файла с помощью метода is_open(). Файл автоматически закроется при выходе из области видимости, но явный вызов close() считается хорошей практикой.
Запись данных в файл
Запись в файл выполняется аналогично выводу в консоль — с помощью оператора <<:
#include <fstream>
int main() {
std::ofstream outputFile("output.txt");
if (!outputFile) {
std::cerr << "Не удалось создать файл!" << std::endl;
return 1;
}
outputFile << "Строка 1" << std::endl;
outputFile << "Строка 2" << std::endl;
outputFile << 42 << std::endl; // числа записываются автоматически как текст
outputFile.close();
return 0;
}
По умолчанию ofstream перезаписывает файл. Чтобы добавить данные в конец, используйте флаг std::ios::app:
std::ofstream outputFile("log.txt", std::ios::app);
Бинарные файлы и произвольный доступ
Для работы с бинарными данными используйте флаг std::ios::binary. Методы read() и write() позволяют работать с блоками памяти:
#include <fstream>
struct Record {
int id;
char name[50];
};
int main() {
// Запись структуры
Record rec = {1, "Пример"};
std::ofstream binOut("data.bin", std::ios::binary);
binOut.write(reinterpret_cast<char*>(&rec), sizeof(rec));
binOut.close();
// Чтение структуры
Record recIn;
std::ifstream binIn("data.bin", std::ios::binary);
binIn.read(reinterpret_cast<char*>(&recIn), sizeof(recIn));
binIn.close();
return 0;
}
Для позиционирования внутри файла используйте seekg() (для ввода) и seekp() (для вывода) с параметрами std::ios::beg, std::ios::cur или std::ios::end.
Современный C++: библиотека std::filesystem
Начиная с C++17, в стандартную библиотеку вошел модуль std::filesystem, который значительно упрощает работу с файловой системой. Он предоставляет удобные функции для манипуляции путями, файлами и директориями.
Работа с путями
Класс std::filesystem::path — центральный элемент библиотеки. Он автоматически обрабатывает разделители путей в зависимости от операционной системы:
#include <iostream>
#include <filesystem>
namespace fs = std::filesystem;
int main() {
// Создание пути
fs::path myPath = "C:/Users/User/Documents/file.txt";
std::cout << "Корневой путь: " << myPath.root_path() << std::endl;
std::cout << "Родительская директория: " << myPath.parent_path() << std::endl;
std::cout << "Имя файла: " << myPath.filename() << std::endl;
std::cout << "Расширение: " << myPath.extension() << std::endl;
std::cout << "Стебель (имя без расширения): " << myPath.stem() << std::endl;
// Конкатенация путей
fs::path newPath = myPath.parent_path() / "newfile.txt";
std::cout << "Новый путь: " << newPath << std::endl;
return 0;
}
Проверка существования и свойств файлов
Библиотека позволяет легко проверить, существует ли файл, является ли он директорией, а также получить его размер и другие атрибуты:
#include <iostream>
#include <filesystem>
namespace fs = std::filesystem;
int main() {
fs::path filePath = "test.txt";
if (fs::exists(filePath)) {
std::cout << "Файл существует." << std::endl;
if (fs::is_regular_file(filePath)) {
std::cout << "Это обычный файл." << std::endl;
std::cout << "Размер: " << fs::file_size(filePath) << " байт" <