HOWTO · C++
CSV-Datei in C++ lesen
Dieser Artikel beschreibt das Lesen einer CSV-Datei in C++
Auf dieser Seite
Um eine einfache, durch Kommas getrennte Datei in C++ zu lesen, lesen Sie mit std::getline jeweils eine Zeile ein und teilen diese anschließend mit einem zweiten Aufruf von std::getline in Felder auf. Das funktioniert, wenn jede physische Zeile genau einen Datensatz enthält und Kommas ausschließlich Felder voneinander trennen. Das folgende Beispiel erstellt selbst eine kleine Eingabedatei. Sie können es daher kompilieren und ausführen, ohne zuvor eine separate Datendatei vorzubereiten.
Eine CSV-Datei mit std::getline und std::istringstream lesen
Das Programm schreibt zunächst zwei Datensätze in grades.csv und öffnet die Datei anschließend zum Lesen. Das äußere std::getline liest eine physische Zeile in einen std::string ein. Das std::istringstream wandelt diese Zeile in einen Stream um; das innere std::getline verwendet dann das Komma als Trennzeichen, um die einzelnen Felder auszulesen. Jeder Datensatz wird als std::vector<std::string> gespeichert, und die Sammlung der Datensätze besteht ebenfalls aus einem Vektor. So bleibt die ursprüngliche Reihenfolge erhalten, ohne für fortlaufend angeordnete Zeilen eine Map mit ganzzahligen Schlüsseln zu benötigen.
Führen Sie dieses eigenständige Programm aus. Die Bezeichnungen und Zeichenketten im unveränderten Codebeispiel sowie die angezeigte Ausgabe bleiben auf Englisch.
#include <algorithm>
#include <cctype>
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
std::string trimEnds(const std::string& value) {
const auto first = std::find_if_not(value.begin(), value.end(),
[](unsigned char ch) { return std::isspace(ch); });
const auto last = std::find_if_not(value.rbegin(), value.rend(),
[](unsigned char ch) { return std::isspace(ch); }).base();
if (first >= last) {
return "";
}
return std::string(first, last);
}
int main() {
const std::string path = "grades.csv";
{
std::ofstream sample(path);
if (!sample) {
std::cerr << "Could not create input file\n";
return 1;
}
sample << "Name, Score\nAda Lovelace, 98 \nGrace Hopper, 95\n";
}
std::ifstream input(path);
if (!input) {
std::cerr << "Could not open input file\n";
return 1;
}
std::vector<std::vector<std::string>> rows;
std::string record;
while (std::getline(input, record)) {
std::istringstream line(record);
std::vector<std::string> fields;
std::string field;
while (std::getline(line, field, ',')) {
fields.push_back(trimEnds(field));
}
rows.push_back(fields);
}
for (const auto& row : rows) {
std::cout << row[0] << " | " << row[1] << '\n';
}
return 0;
}
Das bereitgestellte Ausführungsergebnis für den erfolgreichen Durchlauf lautet:
Name | Score
Ada Lovelace | 98
Grace Hopper | 95
Wenn das Erstellen oder Öffnen einer Datei fehlschlägt, gibt das Programm über std::cerr eine Fehlermeldung aus und liefert den Status 1 zurück. Bei regulärem Ablauf liefert es den Status 0. Prüfen Sie in Ihrem eigenen Programm, ob der Eingabestream erfolgreich geöffnet wurde, bevor Sie daraus lesen. Stammt die Datei von außerhalb Ihres Programms, geben Sie den Pfad aus und behandeln Sie einen Fehler beim Öffnen passend zu den Anforderungen Ihrer Anwendung, statt vorauszusetzen, dass die Datei vorhanden ist.
Leerzeichen in CSV-Feldern behandeln
Wie mit Leerzeichen umzugehen ist, hängt von den Daten ab und wird durch das Aufteilen allein nicht festgelegt. trimEnds entfernt Leerzeichen ausschließlich am Anfang und Ende jedes ausgelesenen Feldes. Leerzeichen innerhalb eines Werts wie Ada Lovelace bleiben erhalten. Die Umwandlung in unsigned char vor dem Aufruf von std::isspace verhindert, dass ein negativer Wert vom Typ char an diese Funktion zur Zeichenklassifizierung übergeben wird.
In manchen Dateien dienen Leerzeichen nach einem Komma nur der Formatierung; in anderen gehören sie zum Feldinhalt. Entfernen Sie Leerzeichen an den Rändern nur, wenn das dem Datenvertrag entspricht, den Sie einhalten müssen. Würden Sie sämtliche Leerzeichen entfernen, würden auch Wörter in Namen oder Adressen zusammengezogen. Das Entfernen an beiden Feldrändern verändert dagegen führende und nachgestellte Inhalte. Wenn Leerzeichen in Ihrer Quelle eine Bedeutung haben, übernehmen Sie das Feld unverändert oder verwenden Sie eine engere Regel, etwa das Entfernen ausschließlich von ASCII-Leerzeichen.
Die Aufteilungslogik hat auch eine einfache Folge für leere Felder: Zwei aufeinanderfolgende Kommas innerhalb einer Zeile ergeben dazwischen eine leere Zeichenkette. Ein Komma am Zeilenende veranlasst diese std::getline-Schleife jedoch nicht, ein abschließendes leeres Feld anzufügen. Behandeln Sie ein abschließendes Trennzeichen ausdrücklich, wenn Ihr Eingabeformat das erfordert. Behalten oder verwerfen Sie leere Werte je nach den erwarteten Spalten. Hat eine Zeile weniger Felder als die Kopfzeile, behandeln Sie das ausdrücklich, bevor Sie auf Elemente der Zeile zugreifen. Die kurze Ausgabeschleife oben setzt voraus, dass beide Spalten vorhanden sind.
Die Grenzen der Trennung anhand von Trennzeichen verstehen
Dieses Verfahren teilt jede physische Zeile an jedem Komma. CSV-Anführungszeichen werden dabei nicht interpretiert. Ein Wert wie "Paris, France" wird beispielsweise in zwei Teile zerlegt, obwohl ein CSV-Leser den Text in Anführungszeichen als ein Feld behandeln sollte. Maskierte Anführungszeichen innerhalb solcher Felder werden ebenfalls nicht dekodiert, und ein Zeilenumbruch innerhalb eines in Anführungszeichen gesetzten Feldes gilt fälschlich als Ende eines Datensatzes. Außerdem kann die Zahl der Felder von Datensatz zu Datensatz variieren. Gehen Sie deshalb nicht davon aus, dass jede Zeile dieselbe Anzahl an Kommas enthält.
Verwenden Sie dieses Verfahren für einfache, kontrollierte Eingaben, deren Regeln genau einen Datensatz pro Zeile und keine Trennzeichen innerhalb von Anführungszeichen garantieren. Für allgemeine CSV-Dateien sollten Sie einen Parser verwenden, der das jeweilige Dateiformat einschließlich der Regeln für Anführungszeichen und Escape-Sequenzen unterstützt. Prüfen Sie auch nach dem Parsen, ob Anzahl und Bedeutung der Felder den Anforderungen Ihrer Anwendung entsprechen. Die hier gezeigten Standard-Stream-Operationen lesen Dateien und Zeilen; die vollständige CSV-Grammatik setzen sie nicht selbst um.