Работа с файловой системой в C++: практическое руководство

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Введение: почему работа с файловой системой в C++ — это база

Любая серьёзная программа рано или поздно сталкивается с необходимостью сохранять данные на диск или читать их оттуда. Конфигурационные файлы, логи, базы данных, пользовательские документы — всё это требует умения взаимодействовать с файловой системой. В C++ для этого есть два основных подхода: классические потоки ввода/вывода (библиотека <fstream>) и современный, более высокоуровневый std::filesystem (начиная с C++17).

В этой статье мы разберём практические сценарии: от простого чтения и записи текстовых файлов до навигации по директориям и обработки ошибок. Все примеры написаны на C++17/20 и готовы к использованию в ваших проектах.

1. Основы: чтение и запись файлов с помощью fstream

Библиотека <fstream> предоставляет три основных класса: ifstream (входной поток — чтение), ofstream (выходной поток — запись) и fstream (двунаправленный поток). Рассмотрим их на примерах.

1.1. Запись в текстовый файл

Самый простой способ записать данные — использовать ofstream. Если файл не существует, он будет создан. Если существует — перезаписан (режим ios::trunc по умолчанию).

#include <iostream>
#include <fstream>
#include <string>

int main() {
    std::ofstream outFile("example.txt");
    if (!outFile) {
        std::cerr << "Ошибка открытия файла для записи\n";
        return 1;
    }

    outFile << "Привет, файловая система C++!\n";
    outFile << "Строка номер 2\n";
    outFile << 42 << " — это число.\n";

    outFile.close(); // закрывать вручную необязательно, но хороший тон
    std::cout << "Данные записаны.\n";
    return 0;
}

1.2. Чтение из текстового файла

Для чтения используем ifstream. Самый надёжный способ — читать построчно через std::getline.

#include <iostream>
#include <fstream>
#include <string>

int main() {
    std::ifstream inFile("example.txt");
    if (!inFile) {
        std::cerr << "Файл не найден или не может быть открыт\n";
        return 1;
    }

    std::string line;
    while (std::getline(inFile, line)) {
        std::cout << "Прочитано: " << line << '\n';
    }

    inFile.close();
    return 0;
}

1.3. Режимы открытия и бинарные файлы

Вы можете комбинировать флаги открытия: ios::app (дозапись), ios::binary (бинарный режим), ios::ate (перейти в конец). Для бинарных файлов используйте методы read() и write().

#include <fstream>
#include <vector>

// Пример: запись и чтение вектора чисел в бинарном формате
void saveVector(const std::string& filename, const std::vector<int>& data) {
    std::ofstream file(filename, std::ios::binary);
    if (!file) return;
    file.write(reinterpret_cast<const char*>(data.data()), data.size() * sizeof(int));
}

std::vector<int> loadVector(const std::string& filename) {
    std::ifstream file(filename, std::ios::binary);
    if (!file) return {};
    file.seekg(0, std::ios::end);
    size_t size = file.tellg() / sizeof(int);
    file.seekg(0, std::ios::beg);
    std::vector<int> data(size);
    file.read(reinterpret_cast<char*>(data.data()), size * sizeof(int));
    return data;
}

2. Современный подход: std::filesystem (C++17)

Начиная с C++17, в стандартную библиотеку вошёл модуль <filesystem>, который позволяет работать с путями, файлами и директориями на высоком уровне, кроссплатформенно.

2.1. Проверка существования и получение информации о файле

Больше никаких системных вызовов! Всё делается через std::filesystem::exists, file_size, last_write_time и другие функции.

#include <iostream>
#include <filesystem>
namespace fs = std::filesystem;

int main() {
    fs::path filePath = "example.txt";

    if (fs::exists(filePath)) {
        std::cout << "Файл: " << filePath << '\n';
        std::cout << "Размер: " << fs::file_size(filePath) << " байт\n";
        std::cout << "Расширение: " << filePath.extension() << '\n';
    } else {
        std::cout << "Файл не найден.\n";
    }
    return 0;
}

2.2. Создание, копирование и удаление файлов и директорий

Основные операции делаются в одну строку. Обратите внимание на обработку ошибок через исключения или перегрузку с std::error_code.

#include <iostream>
#include <filesystem>
namespace fs = std::filesystem;

int main() {
    // Создание директории
    fs::create_directories("backup/2024");

    // Копирование файла
    fs::copy("example.txt", "backup/2024/example_copy.txt", fs::copy_options::overwrite_existing);

    // Переименование (перемещение)
    fs::rename("old_name.txt", "new_name.txt");

    // Удаление файла
    fs::remove("temp.txt");

    // Удаление директории рекурсивно (осторожно!)
    // fs::remove_all("temp_dir");

    std::cout << "Операции выполнены успешно.\n";
    return 0;
}

2.3. Обход директорий: рекурсивный и нерекурсивный

Часто нужно пройтись по всем файлам в папке. std::filesystem предоставляет итераторы: directory_iterator

Блоги

Рекомендаций по книгам