Skip to content
C++ Better Explained
Go back
How to Write to a File in C++ (ofstream Explained)

How to Write to a File in C++

Short answer: open an std::ofstream and use << exactly like std::cout.

std::ofstream file("output.txt");
file << "Hello, file!\n";

The file is created if it does not exist, and closed automatically when the stream goes out of scope.


The Complete Minimal Program

#include <iostream>
#include <fstream>

int main() {
    std::ofstream file("output.txt");

    if (!file) {
        std::cerr << "Could not open output.txt for writing\n";
        return 1;
    }

    file << "Line one\n";
    file << "Line two\n";
    file << "The answer is " << 42 << '\n';
}

Three things to notice: ofstream means output file stream, << behaves identically to cout, and there is no close() call because the destructor handles it.

Always Check the Open Succeeded

Writes can fail for ordinary reasons — a read-only directory, a path that does not exist, a full disk:

std::ofstream file("/some/missing/dir/out.txt");
if (!file) {
    std::cerr << "Open failed\n";
    return 1;
}

Without that check your program runs happily and produces nothing, which is a genuinely confusing bug to chase. Note that ofstream will create a file but not the directories leading to it.

Overwrite vs Append

This is the single biggest gotcha:

std::ofstream file("log.txt");                    // ERASES log.txt
std::ofstream file("log.txt", std::ios::app);     // keeps it, adds to the end

The default mode truncates the file to zero length the moment it opens — before you have written anything. If you want to add to an existing file, see appending to a file with std::ios::app.

Learning C++ properly? The C++ Better Explained Ebook covers file handling, strings and the STL in plain English — 87 pages, just $19.

Writing Numbers and Formatting

The << operator converts numbers to text for you:

int count = 42;
double price = 19.5;

file << count << '\n';
file << price << '\n';          // 19.5

For fixed decimal places, use <iomanip> just as you would for console output:

#include <iomanip>

file << std::fixed << std::setprecision(2);
file << price << '\n';          // 19.50

Writing a Vector or Array

#include <vector>

std::vector<int> scores = {90, 85, 77};

std::ofstream file("scores.txt");
for (int s : scores) {
    file << s << '\n';
}

For comma-separated output, write the separator between items rather than after each one:

for (size_t i = 0; i < scores.size(); ++i) {
    file << scores[i];
    if (i + 1 < scores.size()) file << ',';
}
file << '\n';                   // 90,85,77

That trailing-comma detail matters if anything will read the CSV back.

Writing Structured Records

struct Student {
    std::string name;
    int score;
};

std::vector<Student> students = {{"Ana", 91}, {"Bo", 78}};

std::ofstream file("students.csv");
file << "name,score\n";
for (const auto& s : students) {
    file << s.name << ',' << s.score << '\n';
}

Keep one record per line — that is what makes the file easy to read back line by line.

When Data Actually Reaches the Disk

Output is buffered. It is written when the buffer fills, when you flush, or when the stream closes. If your program crashes before any of those, the file can be empty even though the writes “happened”:

file << "important\n";
file.flush();                   // force it out now

std::endl writes a newline and flushes, which is why it is slower than '\n' inside loops. Use '\n' normally and flush deliberately when durability matters — the difference is covered in endl vs newline.

Closing Early

You rarely need to, but sometimes you want the file released before the end of the block — for example to read it back:

{
    std::ofstream out("data.txt");
    out << "content\n";
}                               // closed here automatically

std::ifstream in("data.txt");   // safe to read now

Using a scope block is cleaner than calling close() manually, because it still works correctly if an exception is thrown.

Checking a Write Succeeded

std::ofstream file("out.txt");
file << "data\n";

if (!file) {
    std::cerr << "Write failed\n";
}

Once a stream enters a failed state it stays there, so a single check after a batch of writes catches any problem in the batch.

Quick Reference

GoalCode
Create / overwritestd::ofstream f("out.txt");
Append insteadstd::ofstream f("out.txt", std::ios::app);
Write textf << "text\n";
Write a numberf << 42;
Two decimal placesf << std::fixed << std::setprecision(2);
Force to diskf.flush();
Check for errorsif (!f) { ... }

Take Your C++ Further

If you want file handling and the rest of the fundamentals explained properly rather than pieced together, the C++ Better Explained Ebook covers it in plain English. Just $19.

👉 Get the C++ Better Explained Ebook — $19


📋

Free Download: The 10 Mistakes Every C++ Beginner Makes

A free 1-page checklist that shows the exact traps that slow down every C++ beginner — so you can avoid them from day one.

🔒 No spam. Unsubscribe anytime.


Share this post on:

Written by

Sahil Bora

Software Engineer. Author and creator of C++ Better Explained.


Previous Post
C++ String Length: size() vs length() vs strlen()
Next Post
Maximum Value of a double in C++ (DBL_MAX Explained)

Keep Learning