Menu

C++ getline: ganze Zeilen einlesen, übersprungene Eingabe beheben, Dateien lesen

Wie std::getline eine ganze Zeile in einen std::string einliest: die Verwendung mit cin, warum es nach cin >> einen leeren String liefert, die Lösungen mit cin.ignore und std::ws, eigene Trennzeichen und das zeilenweise Lesen einer Datei bis zum Dateiende.

Diese Seite enthält ausführbare Editoren - bearbeiten, ausführen und Ausgabe sofort sehen.

Eine ganze Zeile einlesen

cin >> text liest ein einzelnes Wort: Es stoppt beim ersten Leerzeichen. Um alles einzulesen, was der Benutzer bis zur Enter-Taste getippt hat, Leerzeichen inklusive, nimm std::getline aus dem Header <string>:

#include <iostream>
#include <string>

int main() {
    std::string name;
    std::cout << "Full name: ";
    std::getline(std::cin, name);
    std::cout << "Hello, " << name << "!\n";
}

Die Eingabe Ada Lovelace gibt Hello, Ada Lovelace! aus. Mit std::cin >> name käme Hello, Ada! heraus, und Lovelace würde in der Eingabe warten.

Die Signatur lautet:

std::istream& std::getline(std::istream& input, std::string& text);
std::istream& std::getline(std::istream& input, std::string& text, char delim);
  • input ist ein beliebiger Eingabestream: std::cin, eine mit std::ifstream geöffnete Datei oder ein aus einem String gebauter std::istringstream.
  • text wird durch die gelesenen Zeichen ersetzt (nicht ergänzt).
  • delim ist das Zeichen, das das Lesen beendet. Standardmäßig ist es '\n'. Das Trennzeichen wird aus dem Stream entfernt und nicht in text gespeichert.
  • Der Rückgabewert ist der Stream selbst. Deshalb kannst du ihn in einer Bedingung prüfen.

Weil getline bei jedem Eingabestream gleich funktioniert, lesen die ausführbaren Beispiele auf dieser Seite aus einem std::istringstream mit festem Text. So ist die Ausgabe vorhersehbar, ohne dass du etwas tippen musst. Um stattdessen von der Tastatur zu lesen, ersetz die Stream-Variable durch std::cin.

Ausgabe:

[Ada Lovelace]
[Alan Turing]

Die eckigen Klammern zeigen, dass der Zeilenumbruch zu keinem der beiden Strings gehört.

getline nach cin >> liefert einen leeren String

Das ist das häufigste Problem mit getline. Du liest eine Zahl mit >>, danach eine Zeile mit getline, und das getline scheint übersprungen zu werden:

Ausgabe:

age: 30
city: []

input >> age liest die Zeichen 30 und stoppt beim ersten Zeichen, das nicht zu einer Zahl gehören kann, dem Zeilenumbruch. Dieser Zeilenumbruch bleibt im Stream. getline liest dann bis zum nächsten Zeilenumbruch, findet sofort einen und liefert einen leeren String. Mit std::cin wartet das Programm deshalb nie auf die zweite Antwort.

Es gibt zwei übliche Lösungen. Beide gehören zwischen das >> und das getline.

Lösung 1: den Rest der Zeile mit ignore verwerfen.

Ausgabe:

age: 30
city: [Paris]

ignore(n, '\n') wirft Zeichen weg, bis es einen Zeilenumbruch oder n Zeichen entfernt hat, je nachdem, was zuerst eintritt. Den größtmöglichen std::streamsize-Wert zu übergeben heißt „bis zum Zeilenumbruch, egal wie weit weg“. Dabei verschwindet auch alles, was der Benutzer in derselben Zeile nach der Zahl getippt hat, etwa Leerzeichen am Ende. Für die Tastatur lautet die Zeile std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');, und sie braucht #include <limits>.

Ein bloßes std::cin.ignore() verwirft genau ein Zeichen. Das klappt, wenn der Zeilenumbruch direkt auf die Zahl folgt, und scheitert, sobald ein Leerzeichen davor steht.

Lösung 2: führende Leerzeichen mit std::ws überspringen.

Ausgabe:

age: 30
city: [Paris]

std::ws ist ein Manipulator, der an der aktuellen Position alle Leerraumzeichen (Leerzeichen, Tabs, Zeilenumbrüche) verbraucht. input >> std::ws gibt den Stream zurück und kann deshalb direkt in getline stehen. Es überspringt auch Leerzeilen und führende Leerzeichen. Für Namen und Städte ist das praktisch, aber falsch, wenn führende Leerzeichen oder eine leere Zeile eine bedeutsame Eingabe sind. Nimm in diesem Fall ignore.

Bis zum Ende der Eingabe lesen

getline gibt den Stream zurück, und ein Stream wird zu false, sobald ein Lesevorgang fehlschlägt. Wenn keine Zeilen mehr da sind, schlägt getline fehl. Eine while-Schleife darum liest also jede Zeile und hört am Ende auf:

Ausgabe:

1: [apples 3]
2: [bread 1]
3: []
4: [milk 2]
read 4 lines

Zwei Details aus dieser Ausgabe:

  • Eine leere Zeile ist trotzdem ein erfolgreicher Lesevorgang. Sie ergibt einen leeren String, und die Schleife läuft weiter. Nur das Ende der Eingabe stoppt sie.
  • Die letzte Zeile braucht keinen abschließenden Zeilenumbruch. Endete der Text mit milk 2 ohne \n, würde getline sie trotzdem liefern, und erst der nächste Aufruf schlüge fehl.

Mit std::cin liest dieselbe Schleife, bis die Eingabe endet. Das passiert, wenn die Eingabe aus einer Datei umgeleitet wird (./program < data.txt) oder wenn der Benutzer unter macOS und Linux Strg+D drückt, unter Windows Strg+Z und dann Enter.

Vermeide das Muster while (!input.eof()) { std::getline(input, line); ... }. eof() wird erst wahr, nachdem ein Lesevorgang bereits auf das Ende gestoßen ist. Der Rumpf läuft deshalb einmal zu oft, mit einem fehlgeschlagenen Lesevorgang und einem veralteten line. Setz den Aufruf von getline stattdessen in die Bedingung.

Eine Datei Zeile für Zeile lesen

Dateien verwenden dieselbe Schleife. Öffne die Datei mit std::ifstream aus <fstream>, prüf, ob sie geöffnet wurde, und lies:

Ausgabe:

1. Ada 92
2. Grace 88
3. Alan 95

if (!file) fängt eine fehlende Datei oder einen falschen Pfad vor der Schleife ab. Ohne diese Prüfung liest die Schleife einfach null Zeilen, was nach einer leeren Datei aussieht statt nach einem Fehler. Die Datei wird automatisch geschlossen, wenn file am Ende von main den Gültigkeitsbereich verlässt.

Unter Windows gespeicherte Dateien beenden jede Zeile mit \r\n. Liest man eine solche Datei unter Linux oder macOS, entfernt getline das \n, behält aber das \r. Jede Zeile endet dann mit einem unsichtbaren Wagenrücklauf, der Vergleiche wie line == "done" kaputt macht. Entferne ihn nach dem Lesen:

if (!line.empty() && line.back() == '\r') {
    line.pop_back();
}

Ein eigenes Trennzeichen verwenden

Das dritte Argument ändert das Zeichen, das jeden Lesevorgang beendet. Ein häufiger Einsatz ist, eine Zeile an Kommas aufzuteilen. Lies zuerst die Zeile und teile sie dann über einen std::istringstream auf:

Ausgabe:

fields: 3
[Ada Lovelace]
[36]
[London]

Das Komma wird aus dem Stream entfernt und nicht gespeichert, genauso wie der Zeilenumbruch in der Standardform. Beachte, dass ein Zeilenumbruch mit einem eigenen Trennzeichen ein gewöhnliches Zeichen ist. Rufst du std::getline(file, field, ',') direkt auf einer mehrzeiligen Datei auf, kommen das letzte Feld einer Zeile und das erste Feld der nächsten zusammengeklebt zurück, etwa als "London\nGrace Hopper". Wer eine Zeile nach der anderen liest und jede Zeile einzeln aufteilt, vermeidet das. Die Seite zu String-Streams behandelt std::istringstream ausführlicher, auch die Umwandlung von Feldern wie "36" in Zahlen.

std::getline vs. cin.getline

Es gibt auch eine Memberfunktion mit ähnlichem Namen:

char buffer[20];
std::cin.getline(buffer, 20);        // Memberfunktion, char-Array fester Größe

std::string text;
std::getline(std::cin, text);        // freie Funktion, std::string

cin.getline schreibt in ein char-Array und speichert höchstens size - 1 Zeichen plus das abschließende '\0'. Eine längere Zeile versetzt den Stream in einen Fehlerzustand, und jeder spätere Lesevorgang wird übersprungen, bis du std::cin.clear() aufrufst. Bei std::getline mit einem std::string musst du dich um keine Längengrenze kümmern. Nimm es, außer du arbeitest mit einer API, die ein char-Array verlangt.

Häufige Fehler

  • >> und getline mischen, ohne den Zeilenumbruch zu entfernen. Setz ignore(...) oder std::ws dazwischen. Die Seite zu Ein- und Ausgabe zeigt dasselbe Problem mit std::cin und wie du dich erholst, wenn das Einlesen einer Zahl fehlschlägt.
  • getline(std::cin, number) mit einem int aufrufen. getline liest nur in einen std::string. Lies die Zeile und wandle sie dann mit std::stoi oder einem std::istringstream um.
  • Eine Schleife über eof(). Prüf den Aufruf von getline selbst: while (std::getline(input, line)).
  • #include <string> vergessen. std::getline für Strings ist in <string> deklariert. Manche Compiler lassen es über <iostream> durchgehen, und der Code scheitert dann auf einem anderen Compiler.
  • Nicht prüfen, ob eine Datei geöffnet wurde. Ein falscher Pfad lässt std::ifstream stillschweigend fehlschlagen, und die Leseschleife läuft null Mal.

Häufig gestellte Fragen

Was macht getline in C++?

std::getline(stream, text) liest Zeichen aus stream in den std::string text, bis es einen Zeilenumbruch erreicht, und entfernt diesen Zeilenumbruch dann aus dem Stream, ohne ihn zu speichern. Anders als cin >> text behält es Leerzeichen und liest so eine ganze Zeile wie einen vollständigen Namen oder einen Satz.

Warum wird getline nach cin >> in C++ übersprungen?

cin >> number hört beim Zeilenumbruch auf, den du nach der Zahl eingegeben hast, und lässt diesen Zeilenumbruch im Eingabepuffer liegen. Das nächste getline findet ihn sofort und liefert einen leeren String. Verwirf ihn zuerst mit std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); oder ruf std::getline(std::cin >> std::ws, text) auf, um führende Leerzeichen zu überspringen.

Wie verwende ich getline mit einem anderen Trennzeichen als dem Zeilenumbruch?

Übergib das Trennzeichen als drittes Argument: std::getline(stream, field, ','). Es liest bis zum nächsten Komma, speichert den Text davor und verwirft das Komma. Zeilenumbrüche gelten dann als gewöhnliche Zeichen, deshalb kombiniert man das meist mit einem std::stringstream, der aus einer einzelnen Zeile gebaut wird.

Wie lese ich in C++ eine Datei Zeile für Zeile?

Öffne sie mit std::ifstream file("data.txt"); und schreib eine Schleife while (std::getline(file, line)) { ... }. getline gibt den Stream zurück, der zu false wird, sobald keine Zeilen mehr da sind, also endet die Schleife am Dateiende.

Was ist der Unterschied zwischen cin.getline und getline?

std::getline(std::cin, text) ist eine freie Funktion, die in einen std::string liest, der für jede Zeile mitwächst. std::cin.getline(buffer, size) ist eine Memberfunktion, die in ein char-Array fester Größe liest und fehlschlägt, wenn die Zeile länger als size - 1 Zeichen ist. Nimm std::getline mit std::string, außer du hast einen Grund für rohe Arrays.

Illustration der Programmiersprachen bei Coddy

Lerne mit Coddy zu programmieren

LOS GEHT'S