Работа с CSV в C++: библиотеки и практические примеры
CSV (Comma-Separated Values) — один из самых распространённых форматов для хранения табличных данных. В C++ нет встроенной поддержки CSV в стандартной библиотеке (до C++23), поэтому разработчики используют сторонние библиотеки или пишут собственные парсеры. В этой статье мы разберём основные подходы к работе с CSV в C++: от ручного парсинга до готовых решений.
Зачем нужны библиотеки для CSV?
CSV-файлы кажутся простыми, но на практике содержат много подводных камней:
- Экранирование кавычек внутри полей (например,
"Hello, world") - Многострочные значения
- Разные разделители (запятая, точка с запятой, табуляция)
- Разные кодировки (UTF-8, UTF-16, ANSI)
Готовые библиотеки решают эти проблемы и экономят время.
1. Ручной парсинг CSV (без сторонних библиотек)
Для простых случаев можно написать парсер самостоятельно. Пример чтения CSV с помощью std::ifstream и std::getline:
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <string>
std::vector<std::vector<std::string>> parseCSV(const std::string& filename) {
std::vector<std::vector<std::string>> data;
std::ifstream file(filename);
std::string line;
while (std::getline(file, line)) {
std::vector<std::string> row;
std::stringstream ss(line);
std::string cell;
while (std::getline(ss, cell, ',')) {
// Удаляем лишние кавычки (упрощённо)
if (cell.size() >= 2 && cell.front() == '"' && cell.back() == '"') {
cell = cell.substr(1, cell.size() - 2);
}
row.push_back(cell);
}
data.push_back(row);
}
return data;
}
Недостатки: не обрабатывает экранированные запятые внутри кавычек, многострочные поля, пустые строки в конце файла.
2. Библиотека libcsv (C)
libcsv — легковесная библиотека на C, которая корректно парсит CSV в соответствии с RFC 4180. Подходит для проектов, где важна производительность.
Установка (Linux): sudo apt-get install libcsv-dev
#include <csv.h>
#include <iostream>
#include <vector>
void cb1(void *s, size_t len, void *data) {
std::string field(static_cast<char*>(s), len);
static_cast<std::vector<std::string>*>(data)->push_back(field);
}
void cb2(int c, void *data) {
if (c == CsvRowEnd) {
// Конец строки — выводим
auto& row = *static_cast<std::vector<std::string>*>(data);
for (const auto& f : row) std::cout << f << " | ";
std::cout << "\n";
row.clear();
}
}
int main() {
FILE *fp = fopen("data.csv", "r");
if (!fp) return 1;
struct csv_parser p;
csv_init(&p, CSV_STRICT);
std::vector<std::string> row;
char buf[1024];
size_t bytes_read;
while ((bytes_read = fread(buf, 1, sizeof(buf), fp)) > 0) {
csv_parse(&p, buf, bytes_read, cb1, cb2, &row);
}
csv_fini(&p, cb1, cb2, &row);
csv_free(&p);
fclose(fp);
return 0;
}
3. Библиотека rapidcsv (C++ header-only)
rapidcsv — современная C++17 библиотека, работающая только с заголовочными файлами. Поддерживает автоматическое определение разделителя, работу с разными типами данных и запись CSV.
Установка: скопировать rapidcsv.h в проект.
#include "rapidcsv.h"
#include <iostream>
#include <vector>
int main() {
// Чтение CSV
rapidcsv::Document doc("example.csv");
// Получить столбец по индексу (все строки)
std::vector<int> ids = doc.GetColumn<int>(0);
// Получить ячейку по имени столбца и номеру строки
std::string name = doc.GetCell<std::string>("Name", 2);
// Запись нового CSV
rapidcsv::Document out; Также в библиотеке
Работа с изображениями в Go (Golang): image, draw, imaging, resize
Bogus для C# (.NET): полное руководство с примерами
Генерация PDF отчётов на Go (Golang): библиотеки и примеры