HOWTO · C++

How to Read CSV File in C++

This article demonstrates how to read a CSV file in C++

On this page

To read a simple comma-delimited file in C++, read one line at a time with std::getline, then split that line into fields with a second std::getline call. This approach works when each physical line is one record and commas only separate fields. The example below creates its own small input file so you can compile and run it without preparing a separate data file.

Read a CSV file with std::getline and std::istringstream

The program first writes two records to grades.csv, then opens the file for reading. The outer std::getline reads a physical line into a std::string; the std::istringstream turns that line into a stream, and the inner std::getline uses a comma delimiter to extract each field. Each record becomes a std::vector<std::string>, and the collection of records is another vector. This keeps the original order and avoids using an integer-keyed map when the rows are simply sequential.

Run this self-contained program:

#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;
}

The supplied execution result for the successful run is:

Name | Score
Ada Lovelace | 98
Grace Hopper | 95

Both file-creation and file-open failures write an error to std::cerr and return status 1; the normal path returns status 0. In your own program, check whether the input stream opened successfully before reading. If the file comes from outside your program, report the path and handle an open failure according to your application’s needs rather than assuming the file exists.

Handle whitespace in CSV fields

Whitespace is a data-handling choice, not an automatic part of splitting. trimEnds removes whitespace only from the beginning and end of each extracted field. It preserves spaces inside a value such as Ada Lovelace. The conversion to unsigned char before calling std::isspace avoids passing a negative plain char value to that character-classification function.

Some files treat spaces after a comma as formatting, while others treat them as part of the field. Apply trimming only if it matches the data contract you need to preserve. Removing every whitespace character would also join words within a name or address; trimming both ends changes leading and trailing content. If whitespace has meaning in your source, keep the field as read or implement a narrower rule, such as trimming only ASCII spaces.

The splitting loop also has a simple consequence for empty fields: a pair of adjacent commas between other content produces an empty string between them. A comma at the end does not make this std::getline loop append a final empty field; handle a trailing delimiter explicitly if your input format requires one. Keep or reject empty values based on the expected columns. A line with fewer fields than the header should be handled explicitly before indexing into its row; the short display loop above assumes both columns are present.

Understand the limits of delimiter-based parsing

This method divides every physical line at every comma. It does not interpret CSV quoting. For example, a value such as "Paris, France" is split into two pieces, even though a CSV reader should treat the quoted text as one field. Escaped quotation marks inside quoted fields are also not decoded, and a newline inside a quoted field is mistaken for the end of a record. Records can also contain different numbers of fields, so do not rely on every line having the same number of commas.

Use this approach for controlled, simple input whose rules guarantee one record per line and no quoted delimiters. For general CSV files, use a CSV parser that implements the dialect you receive, including its quote and escape rules. After parsing, still validate the number and meaning of fields your application requires. The standard stream operations shown here handle file and line reading; they do not by themselves provide the full CSV format grammar.