Допустим, у нас есть таблица с данными о студентах. Для строки таблицы напишем структуру.
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]) });