Универсальная обработка csv на С++

Конкретная таблица

Допустим, у нас есть таблица с данными о студентах. Для строки таблицы напишем структуру.

struct Student {
    int id;
    std::string name; // UTF-8
    int score;
};

И добавим функцию

// Собираем структуру из строк-ячеек
Student map_student(const std::vector<std::string>& r)
{
    Student s;
    s.id = std::stoi(r[0]);
    s.name = r[1];      // UTF-8
    s.score = std::stoi(r[2]);
    return s;
}

Теперь чтение всего файла может выглядеть так:

std::vector<Student> read_students_csv(const std::string& filename)
{
    std::ifstream file(filename, std::ios::binary);
    std::vector<Student> result;
    std::string line;
    // Читаем все строки
    while (std::getline(file, line)) {

        std::vector<std::string> fields;
        std::stringstream ss(line);
        std::string cell;

        while (std::getline(ss, cell, ',')) {
            fields.push_back(cell);
        }

        result.push_back(map_student(fields));
    }

    return result;
}

Функция возвращает вектор, заполненный студентами.

Делаем функцию чтения универсальной

Для того, чтобы можно было читать любой csv, нужно изменить функцию так: сделать ее шаблонной и передавать указатель на функцию мэппинга.

template <typename T, typename Mapper>
std::vector<T> read_csv(const std::string& filename, Mapper mapper
{
    ...
    while (std::getline(file, line)) {

        std::vector<std::string> fields;
        std::stringstream ss(line);
        std::string cell;
        // Разбиваем по ячейкам-строкам
        while (std::getline(ss, cell, ',')) {
            fields.push_back(cell);
        }
        // Вызывается мэппер для нужной структуры
        result.push_back(mapper(fields));
    }
    return result;
}

Вызвать ее можно так:

auto students = read_csv<Student>("students.csv", map_student);

Для другой структуры тот же самый код тоже будет работать!

Лямбдой тоже можно

Создавать функцию map_student нет необходимости. Можно использовать синтаксис лямбд.

auto students = read_csv<Student>("students.csv", 
[](const std::vector<std::string>& r) -> Student 
{ return Student{std::stoi(r[0]), r[1], std::stoi(r[2]) });