Работа с файловой системой в C++: от основ до продвинутых техник
Работа с файловой системой — одна из ключевых задач в любом серьёзном приложении на C++. Будь то чтение конфигурационных файлов, запись логов, обработка больших объёмов данных или управление проектами — умение правильно взаимодействовать с файлами и директориями необходимо каждому C++ разработчику. В этой статье мы разберём основные подходы к работе с файловой системой в C++, начиная от классических C-функций и заканчивая современным std::filesystem, появившимся в C++17.
Мы рассмотрим практические примеры, которые помогут вам не только понять теорию, но и сразу применить полученные знания в реальных проектах. Все примеры кода протестированы и готовы к использованию.
1. Основы работы с файлами: потоки ввода/вывода
Наиболее распространённый способ работы с файлами в C++ — использование библиотеки <fstream>. Она предоставляет три основных класса:
std::ifstream— для чтения из файла (input file stream);std::ofstream— для записи в файл (output file stream);std::fstream— для чтения и записи (file stream).
Чтение из файла
Для чтения данных из файла создайте объект std::ifstream и откройте файл. Самый простой способ прочитать файл построчно — использовать функцию std::getline:
#include <iostream>
#include <fstream>
#include <string>
int main() {
std::ifstream inputFile("example.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() — это важная практика, которая предотвращает краш программы при отсутствии файла.
Запись в файл
Для записи данных используется std::ofstream. По умолчанию файл создаётся заново (режим trunc). Если нужно дописать данные в конец, используйте флаг std::ios::app:
#include <iostream>
#include <fstream>
int main() {
std::ofstream outputFile("output.txt", std::ios::app);
if (!outputFile.is_open()) {
std::cerr << "Ошибка создания файла!" << std::endl;
return 1;
}
outputFile << "Строка 1" << std::endl;
outputFile << "Строка 2" << std::endl;
outputFile << "Число: " << 42 << std::endl;
outputFile.close();
return 0;
}
Работа с бинарными файлами
Для бинарных данных используйте методы read() и write() с указанием размера блока:
#include <iostream>
#include <fstream>
#include <cstring>
struct Record {
int id;
char name[50];
double value;
};
int main() {
// Запись
Record r = {1, "Тестовая запись", 3.14};
std::ofstream binOut("data.bin", std::ios::binary);
binOut.write(reinterpret_cast<const char*>(&r), sizeof(r));
binOut.close();
// Чтение
Record r2;
std::ifstream binIn("data.bin", std::ios::binary);
binIn.read(reinterpret_cast<char*>(&r2), sizeof(r2));
binIn.close();
std::cout << "ID: " << r2.id << ", Name: " << r2.name
<< ", Value: " << r2.value << std::endl;
return 0;
}
2. Современный подход: библиотека std::filesystem (C++17)
Начиная с C++17, в стандартную библиотеку была добавлена <filesystem>, которая значительно упрощает работу с файловой системой. Она предоставляет удобные функции для работы с путями, директориями и файлами.
Проверка существования файла и получение информации
#include <iostream>
#include <filesystem>
namespace fs = std::filesystem;
int main() {
fs::path filePath("example.txt");
if (fs::exists(filePath)) {
std::cout << "Файл существует" << std::endl;
std::cout << "Размер: " << fs::file_size(filePath) << " байт" << std::endl;
std::cout << "Расширение: " << filePath.extension() << std::endl;
} else {
std::cout << "Файл не найден" << std::endl;
}
return 0;
}
Работа с директориями
Создание, удаление и обход директорий — одни из самых частых операций. Вот как это делается с помощью std::filesystem:
#include <iostream>
#include <filesystem>
#include <vector>
namespace fs = std::filesystem;
int main() {
// Создание директории
fs::create_directory("my_folder");
// Создание вложенных директорий
fs::create_directories("my_folder/sub_folder/deep");
// Обход всех файлов в директории
std::cout << "Содержимое текущей директории:" << std::endl;
for (const auto& entry : fs::directory_iterator(".")) {
std::cout << (entry.is_directory() ? "[DIR] " : "[FILE] ")
<< entry.path().filename() << std::endl;
}
// Рекурсивный обход
std::cout << "\nРекурсивный обход:" << std::endl;
for (const auto& entry : fs::recursive_directory_iterator("my_folder")) {
std::cout << entry.path() << std::endl;
}
// Удаление директории (пустой)
fs