C++ / CONSOLE AND FILE INPUT-OUTPUT
Line-oriented input with getline and its pitfalls
Read whole lines and custom-delimited fields with std::getline, and fix the empty-line bug that appears when you mix it with operator>>.
What you will learn
- Loop with while (std::getline(in, line)); never test in.eof() before the read.
- After any >> read, call in.ignore(max, '\n') before the first getline.
- Split fields with getline(in, s, ',') and keep empty fields as empty strings.
- Strip a trailing '\r' when reading text that was written on Windows.
Understanding Line-oriented input with getline and its pitfalls
std::getline(stream, str) copies characters out of the stream into str until it reaches the delimiter, which is '\n' unless you pass a third argument. The delimiter is extracted from the stream but not stored in the string, so a line arrives without its terminator and a blank line arrives as an empty string. The string resizes itself to whatever the line held, which is why getline is the right tool for anything containing spaces: operator>> would stop at the first one. It is declared in <string>, not <iostream>, even though its first argument is a stream.
The mental model that prevents most getline bugs is that a stream is one queue of bytes with a single read position, and different operations leave that position in different places. Formatted extraction with >> reads as far as the token goes and then stops, deliberately leaving the character that ended the token, usually the newline you typed, still in the queue. getline instead reads through its delimiter and swallows it, so cin >> n; followed by getline(cin, s) hands getline a newline as its very first character: it stops immediately, returns an empty string, and the stream stays perfectly healthy, which is why nothing looks broken. Calling cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n') in between discards the rest of that line and realigns the position.
getline returns the stream, and a stream converts to true while failbit is clear, which is what makes while (std::getline(in, line)) the canonical line loop. Once its sentry succeeds, getline erases the string and then extracts; if it can extract nothing because the stream is already at the end, it sets eofbit and failbit together and the loop stops with line empty. Testing in.eof() before the read cannot work, because a final line that ends on its delimiter never touches end-of-file, so eof() is still false and the loop runs one extra time on a line that was never read.
<iostream>
<limits>
<sstream>
<string>
int main() {
// operator>> stops in front of the newline; getline reads through it.
std::istringstream broken("42\nAda Lovelace\n");
int id = 0;
std::string name;
broken >> id;
std::getline(broken, name);
std::cout << "broken: id=" << id << " name=[" << name << "]\n";
std::istringstream repaired("42\nAda Lovelace\n");
repaired >> id;
repaired.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
std::getline(repaired, name);
std::cout << "repaired: id=" << id << " name=[" << name << "]\n";
// The delimiter is consumed but never stored, so a blank line arrives empty.
std::istringstream lines("one\n\nthree\n");
std::string line;
int n = 0;
while (std::getline(lines, line)) {
std::cout << ++n << ": [" << line << "] size=" << line.size() << '\n';
}
std::cout << "eof=" << lines.eof() << " fail=" << lines.fail() << '\n';
}
getline consumes and discards its delimiter while operator>> leaves that character in the stream, and that asymmetry is the source of nearly every getline surprise.
Worked examples
Splitting on a delimiter other than newline
Shows that a custom delimiter keeps empty fields, which operator>> cannot do.
<iostream>
<sstream>
<string>
int main() {
std::istringstream row("ada,,1815,London");
std::string field;
int col = 0;
while (std::getline(row, field, ',')) {
std::cout << "col " << col++ << ": [" << field << "]\n";
}
std::cout << "fields=" << col << '\n';
}
Example explained
Line 1The third argument replaces the newline as the delimiter; nothing else about getline changes.
Line 2The gap between the two commas comes back as an empty string, information that >> cannot express at all.
Line 3Reading London reaches end-of-file with characters stored, so eofbit is set but failbit is not and the body still runs for that field.
Line 4Input ending in the delimiter, such as ada,1815, yields two fields rather than three: the last call extracts nothing, sets failbit, and the empty tail is lost.
The stray carriage return from CRLF text
Demonstrates why a line read from a Windows-written file compares unequal to the text you expect.
<iostream>
<sstream>
<string>
int main() {
std::istringstream file("alpha\r\nbeta\r\n");
std::string line;
while (std::getline(file, line)) {
std::cout << "raw=" << line.size();
if (!line.empty() && line.back() == '\r') {
line.pop_back();
}
std::cout << " clean=[" << line << "] size=" << line.size() << '\n';
}
}
Example explained
Line 1getline splits on '\n' only, so the '\r' of a CRLF pair stays on the end of the string.
Line 2size() reports 6 for a five-letter word, and that invisible byte is what makes line == "alpha" false.
Line 3Testing line.back() == '\r' and calling pop_back() is the whole fix; the !line.empty() guard matters because back() on an empty string is undefined.
Line 4A Windows text-mode stream translates CRLF for you, so this only bites when the file crosses platforms or is opened in binary mode.
Free getline versus the member istream::getline
Shows how the char-array overload truncates, sets failbit, and leaves the rest of the line in the stream.
<iostream>
<sstream>
<string>
int main() {
std::istringstream in("abcdefgh\nnext\n");
char buf[5];
in.getline(buf, 5);
std::cout << "buf=[" << buf << "] gcount=" << in.gcount()
<< " fail=" << in.fail() << '\n';
in.clear();
std::string tail;
std::getline(in, tail);
std::cout << "tail=[" << tail << "]\n";
}
Example explained
Line 1in.getline(buf, 5) is the member function: it stores at most n-1 = 4 characters plus a terminating '\0'.
Line 2Reaching the size limit before the delimiter sets failbit, and gcount() reports the 4 characters actually extracted.
Line 3Because the newline was never reached, efgh\n is still queued, so every later read continues in the middle of that line.
Line 4clear() is required first, since a stream with failbit set refuses further reads; the free getline then returns the remainder into a string that sizes itself.
Important notes
in >> std::ws also clears a pending newline, but it skips every following blank line and any leading spaces, so use it only when blank lines and indentation are not data; ignore(max, '\n') throws away just the remainder of the current line.
std::getline for std::string is declared in <string>, so code that includes only <iostream> may build with one standard library and fail with another.
Common mistakes
Writing cin >> age; then std::getline(cin, name);: the newline that ended the number is still queued, getline stops on it instantly, and name is empty while the program looks like it skipped a prompt.
Writing while (!in.eof()) { std::getline(in, line); use(line); }: eofbit is only set by the read that fails, so a file ending in a newline runs the body one extra time on an empty line.
Calling getline on a stream that already has failbit set, for example after a failed >> or a truncating in.getline(buf, n): the sentry fails, the string is left untouched, and the previous line is processed again until you call clear().
Try it yourself
Change, predict, then run
Create an istringstream holding "3\nfirst\nsecond\nthird\n", read the leading count with >>, then read exactly that many whole lines with getline and print them numbered. You will need one ignore call to stop line 1 from coming back empty.
Open the C++ workspaceCheck your understanding
A stream holds "7\nhello world\n". The code runs in >> n; and then std::getline(in, s);. Why does s end up empty?
- operator>> left the newline after 7 in the stream, and getline saw that newline as its first character and stopped there
- getline skips leading whitespace first, and after skipping it found nothing left to read on that line
- operator>> consumed the whole first line including the newline, so getline started past the text it wanted
- the extraction put the stream into a fail state, so getline refused to read anything
Show answer
operator>> stops as soon as it meets a character that cannot belong to an integer, leaving '\n' at the read position; getline treats that newline as an immediate end of line, consumes it, and returns an empty string with the stream still good. Option 2 is tempting because formatted extraction does skip leading whitespace, but getline is an unformatted read and skips nothing, which is exactly what lets it report blank lines.