Чтение и запись файлов в C++: ifstream, ofstream и fstream — практическое руководство
Этот материал — ответ на частый запрос «C++ чтение и запись файлов: ifstream/ofstream примеры». Разберём базовые классы потоков, режимы открытия, безопасные паттерны, чтение построчно и побайтно, а также двоичные файлы и обработку ошибок. Все примеры компилируются и подходят для начинающих и продолжающих.
Базовые классы для работы с файлами
- std::ifstream — чтение (input file stream)
- std::ofstream — запись (output file stream)
- std::fstream — и чтение, и запись
RAII освобождает файл автоматически при разрушении объекта потока — закрывать вручную обычно не требуется.
Открытие файла и проверка ошибок
#include <fstream>
#include <iostream>
#include <string>
int main() {
std::ifstream in("input.txt");
if (!in.is_open()) {
std::cerr << "Не удалось открыть input.txt\n";
return 1;
}
std::string line;
size_t lines = 0;
while (std::getline(in, line)) {
++lines;
}
std::cout << "Строк в файле: " << lines << "\n";
}
Если файл не открылся, проверьте путь, права и существование каталога.
Режимы открытия файла
- std::ios::in — чтение (по умолчанию для ifstream)
- std::ios::out — запись (по умолчанию для ofstream)
- std::ios::app — дозапись в конец
- std::ios::trunc — обрезать файл при открытии
- std::ios::binary — двоичный режим (важно на Windows)
std::ofstream out("log.txt", std::ios::app); // дозапись
std::ifstream binIn("data.bin", std::ios::binary); // двоичный
std::fstream both("db.dat", std::ios::in | std::ios::out | std::ios::binary);
На Windows текстовый режим меняет переводы строк. Для двоичных данных всегда используйте std::ios::binary.
Чтение построчно и по словам
#include <fstream>
#include <iostream>
#include <string>
#include <limits>
int main() {
std::ifstream in("config.txt");
if (!in) return 1;
// Пример: сначала число, потом строка с пробелами
int version = 0;
in >> version; // читает до пробела/перевода строки
in.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
std::string title;
std::getline(in, title); // корректно прочитает всю строку
std::cout << "version=" << version << ", title=" << title << "\n";
}
Альтернатива для пропуска пробелов перед getline: std::getline(in >> std::ws, line).
Запись в файл: переносы строк и буферизация
#include <fstream>
#include <string>
int main() {
std::ofstream out("report.txt"); // trunc по умолчанию
if (!out) return 1;
out << "Отчёт за день" << '\n'; // '\n' быстрее, чем std::endl
out << "Всего записей: " << 42 << '\n';
}
Совет: используйте '\n' вместо std::endl, чтобы не форсировать лишний flush.
Работа с путями: std::filesystem
#include <filesystem>
#include <fstream>
int main() {
namespace fs = std::filesystem;
fs::path p = "output/logs/app.log";
fs::create_directories(p.parent_path());
std::ofstream out(p, std::ios::app);
out << "Started" << '\n';
}
Так вы избежите ошибок «No such file or directory», если каталога ещё нет.
Считать весь файл в строку
#include <fstream>
#include <string>
#include <iterator>
std::string read_all(const std::string& path) {
std::ifstream in(path, std::ios::binary);
if (!in) return {};
return std::string(
std::istreambuf_iterator<char>(in),
std::istreambuf_iterator<char>()
);
}
Подходит для небольших файлов (напр., конфигураций, шаблонов).
Двоичные файлы: чтение и запись
#include <fstream>
#include <type_traits>
#include <vector>
struct Record {
int id;
double value;
};
static_assert(std::is_trivially_copyable_v<Record>);
int main() {
// Запись
{
std::ofstream out("data.bin", std::ios::binary);
Record r{123, 45.67};
out.write(reinterpret_cast<const char*>(&r), sizeof(r));
}
// Чтение
{
std::ifstream in("data.bin", std::ios::binary);
Record r{};
in.read(reinterpret_cast<char*>(&r), sizeof(r));
}
}
Такой подход быстрый, но не переносимый между архитектурами из-за выравнивания/эндианности. Для устойчивых форматов используйте сериализацию (JSON/CBOR/Protobuf и пр.) или фиксированный протокол.
Исключения вместо ручных проверок
#include <fstream>
#include <iostream>
int main() {
try {
std::ifstream in;
in.exceptions(std::ios::failbit | std::ios::badbit);
in.open("input.txt", std::ios::binary);
std::string s((std::istreambuf_iterator<char>(in)), {});
std::cout << "Прочитано байт: " << s.size() << "\n";
} catch (const std::ios_base::failure& e) {
std::cerr << "Ошибка ввода-вывода: " << e.what() << "\n";
}
}
Маска исключений заставляет поток бросать исключение при ошибках открытия/чтения/записи.
Мини-пример: копирование файла с подсчётом строк
#include <fstream>
#include <iostream>
#include <string>
int main(int argc, char** argv) {
if (argc < 3) {
std::cerr << "Usage: copy_with_count <src> <dst>\n";
return 1;
}
std::ifstream in(argv[1]);
if (!in) { std::cerr << "Не открыть: " << argv[1] << "\n"; return 1; }
std::ofstream out(argv[2]);
if (!out) { std::cerr << "Не создать: " << argv[2] << "\n"; return 1; }
std::string line; size_t n = 0;
while (std::getline(in, line)) {
++n;
out << line << '\n';
}
std::cout << "Скопировано. Строк: " << n << "\n";
}
Частые ошибки и как их избежать
- Не проверяете открытие файла — всегда проверяйте
is_open()или настраивайте исключения. - Смешиваете
>>иstd::getline— используйтеignoreилиstd::ws. - Забываете
std::ios::binaryдля двоичных данных на Windows — файлы портятся из-за перевода строк. - Чрезмерно используете
std::endl— замените на'\n'для производительности. - Записываете «сырые» структуры в двоичный файл для обмена между системами — лучше используйте переносимые форматы.
Вывод
Теперь вы умеете надёжно открывать файлы, читать по строкам, писать отчёты, работать с двоичными данными и обрабатывать ошибки. Эти приёмы покрывают 80% типовых задач «чтение и запись файлов в C++» и помогут избегать подводных камней на Windows и Linux.
Хотите системно закрыть пробелы в языке и прокачать практику? Рекомендую посмотреть программу и стартовать обучение по курсу «Программирование на C++ с Нуля до Гуру» — узнать детали курса и забрать первые уроки.
-
Создано 12.01.2026 17:01:43
-
Михаил Русаков

Комментарии (0):
Для добавления комментариев надо войти в систему.
Если Вы ещё не зарегистрированы на сайте, то сначала зарегистрируйтесь.