/commands/cpp/read-file-line-by-line
c++ read a file line by line with std::getline and ifstream
- tool
- C++
- versions
- C++98+, std::filesystem: C++17
- os
- any
To read a text file line by line in C++, open it with std::ifstream file("data.txt"), check that it opened, and loop with while (std::getline(file, line)). Each iteration gets one line without the \n; the loop ends at the end of the file or on a read error. The file is closed automatically when file goes out of scope.
Read lines, skip blanks, count
The example uses Clang 20 from the ziglang package on PyPI (with libc++), run through uv; cxx stands for it. On your machine, write g++ or clang++:
cxx() { uv run -q --no-project --python 3.13 --with ziglang==0.15.1 python -m ziglang c++ -fno-color-diagnostics "$@"; }
printf 'alpha\r\nbeta\n\ngamma' > data.txt
cat > lines.cpp <<'EOF'
#include <fstream>
#include <iostream>
#include <string>
int main() {
std::ifstream file("data.txt", std::ios::binary); // binary: see the \r on every platform
if (!file) {
std::cerr << "cannot open data.txt\n";
return 1;
}
std::string line;
int n = 0, blank = 0;
while (std::getline(file, line)) {
bool cr = !line.empty() && line.back() == '\r';
if (cr) line.pop_back(); // strip a Windows line ending
if (line.empty()) { ++blank; continue; }
std::cout << ++n << ": [" << line << "]" << (cr ? " (had \\r)" : "") << '\n';
}
std::cout << n << " lines, " << blank << " blank\n";
std::ifstream missing("missing.txt");
std::cout << "missing.txt open: " << std::boolalpha << missing.is_open() << '\n';
}
EOF
cxx -std=c++17 -Wall -Wextra lines.cpp -o lines && ./linesOutput
1: [alpha] (had \r) 2: [beta] 3: [gamma] 3 lines, 1 blank missing.txt open: false
std::getline removes the \n but not a \r, so lines from a file with Windows line endings end in \r unless the stream translates them (text mode on Windows does). The last line is returned even without a final newline. Opening a missing file does not throw; the stream simply tests false.
Variants
| Code | What it does |
|---|---|
| while (std::getline(file, line)) | One line per iteration, without the newline |
| std::getline(file, field, ',') | Read up to another delimiter, for example one CSV field |
| while (file >> word) | Read whitespace-separated words instead of lines |
| std::getline(std::cin, line) | Read standard input line by line |
| file.exceptions(std::ios::badbit) | Throw on real I/O errors instead of silently stopping |
| std::filesystem::exists(path) | Check the path before opening (C++17, <filesystem>) |
Pitfalls
- Not checking the open.
if (!file)after construction catches a missing file or missing permissions; without it the loop simply never runs and the program looks like it read an empty file. while (!file.eof()). The classic bug: the last line is processed twice or an empty line is processed at the end. Use thegetlinecall as the condition.- Mixing
>>and getline. Afterstd::cin >> n, the newline stays in the stream and the nextgetlinereturns an empty line; callstd::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n')in between. - Relative paths are resolved against the working directory of the process, which in an IDE is often the build folder.
- Splitting or converting each line: see split a string by delimiter and convert a string to int.
Confirm it worked
Print the line count and compare it with your editor's line count; printing each line between brackets, as above, makes trailing spaces and \r characters visible.
FAQ
Does std::getline keep the newline?
No. It reads up to the delimiter (\n by default), removes it from the stream and does not store it. A \r from a Windows file opened on Linux does stay at the end of the line.
Why does my loop read the last line twice?
Because it tests eof() before reading: while (!file.eof()) { getline(file, line); ... }. Use the getline call itself as the loop condition, while (std::getline(file, line)), which stops as soon as a read fails.
How do I read the whole file into one string?
std::string text((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>()); or read it with a std::stringstream: ss << file.rdbuf().