Работа с CSV в C++: библиотека libcsv и стандартные средства

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Работа с CSV в C++: от ручного парсинга до библиотеки libcsv

Формат CSV (Comma-Separated Values) остаётся одним из самых популярных способов обмена табличными данными. В C++ нет встроенной поддержки CSV, но существует несколько эффективных подходов: ручной парсинг с использованием стандартных средств и специализированные библиотеки, такие как libcsv. В этой статье мы разберём оба метода, их преимущества и недостатки, а также приведём рабочие примеры кода.

Что такое CSV и его особенности

CSV — это текстовый формат, где каждая строка соответствует записи, а поля разделяются запятыми. Основные сложности:

  • Поля могут содержать запятые, если они заключены в кавычки ("Hello, world");
  • Кавычки внутри поля экранируются дублированием ("");
  • Разделитель может быть другим символом (точка с запятой, табуляция);
  • Строки могут содержать символы новой строки внутри кавычек.

Поэтому простое разбиение строки по запятым часто приводит к ошибкам. Рассмотрим два надёжных способа.

Метод 1: Ручной парсинг с помощью std::stringstream

Подходит для простых CSV без вложенных кавычек и переносов строк. Реализация состоит из двух функций: чтения одного поля и разбора всей строки.

#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <string>

// Разделение строки CSV на поля с учётом кавычек
std::vector<std::string> parseCSVLine(const std::string& line) {
    std::vector<std::string> fields;
    std::string field;
    bool inQuotes = false;

    for (size_t i = 0; i < line.size(); ++i) {
        char c = line[i];
        if (c == '"' && !inQuotes) {
            inQuotes = true;
        } else if (c == '"' && inQuotes) {
            // Проверка на экранированные кавычки
            if (i + 1 < line.size() && line[i + 1] == '"') {
                field += '"';
                ++i; // пропускаем следующую кавычку
            } else {
                inQuotes = false;
            }
        } else if (c == ',' && !inQuotes) {
            fields.push_back(field);
            field.clear();
        } else {
            field += c;
        }
    }
    fields.push_back(field); // последнее поле
    return fields;
}

int main() {
    std::ifstream file("data.csv");
    std::string line;

    while (std::getline(file, line)) {
        auto fields = parseCSVLine(line);
        for (const auto& f : fields) {
            std::cout << "[" << f << "] ";
        }
        std::cout << std::endl;
    }
    return 0;
}

Плюсы: нет внешних зависимостей, полный контроль над логикой.
Минусы: не обрабатывает многострочные поля (значение, содержащее \n), требует ручной обработки ошибок.

Метод 2: Библиотека libcsv

libcsv — легковесная C-библиотека для парсинга CSV, написанная на C, но отлично работающая из C++. Она корректно обрабатывает все стандартные случаи: кавычки, экранирование, многострочные поля, пользовательские разделители.

Установка

Скачайте исходники с SourceForge или установите через пакетный менеджер:

# Linux (Debian/Ubuntu)
sudo apt-get install libcsv-dev

# macOS
brew install libcsv

Пример чтения CSV с libcsv

#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <cstring>
#include <csv.h>

// Структура для хранения данных
struct CSVData {
    std::vector<std::vector<std::string>> rows;
    std::vector<std::string> currentRow;
};

// Callback при обнаружении нового поля
void cb1(void *s, size_t len, void *data) {
    CSVData *csvData = static_cast<CSVData*>(data);
    csvData->currentRow.push_back(std::string(static_cast<char*>(s), len));
}

// Callback при завершении строки
void cb2(int c, void *data) {
    CSVData *csvData = static_cast<CSVData*>(data);
    csvData->rows.push_back(csvData->currentRow);
    csvData->currentRow.clear();
}

int main() {
    std::ifstream file("data                    

Рекомендаций