прочитать CSV-файл и добавить все его данные в вектор в C ++ - PullRequest
0 голосов
/ 20 февраля 2020

Например, чтобы добавить следующие данные CSV:

enter image description here

Я пытаюсь добавить файл CSV в векторный массив строки 2D и получить сумму каждого столбца. Следующая программа не работала должным образом,

vector<string> read_csv(string filename){

    vector<string> result;
    fstream fin;
    fin.open(filename, ios::in);

    if(!fin.is_open())
        throw std::runtime_error("Could not open file");

    std::string line, colname;
    int val;

    // Read the column names
    if(fin.good())
    {
        std::getline(fin, line);
        std::stringstream ss(line);
        while(std::getline(ss, colname, ',')){
            result.push_back(colname);
            cout << colname << endl;
        }
    }

    while(std::getline(fin, line))
    {
        std::stringstream ss(line);
        int colIdx = 0;
        while(ss >> val){

            if(ss.peek() == ',') ss.ignore();
            colIdx++;
        }
    }
    fin.close();
    return result;
}

, когда я пытался go через вектор, я не получил должного результата. Он показывал только имена столбцов.

for (int i = 0; i < vectorCsv.size(); ++i) 
{
        cout << vectorCsv[i] << endl;
}

Я не смог найти, ошибка в функции read_csv () или в forl oop. Спасибо за внимание к этой проблеме.

Ответы [ 3 ]

2 голосов
/ 20 февраля 2020
  1. Не пытайтесь создавать векторы по std::string с, это, вероятно, не очень эффективно - каждая строка выделяется и удаляется отдельно.
  2. Не читайте CSV-файлы самостоятельно - ты изобретаешь колесо. Используйте существующую библиотеку. Вот вопрос о том, как найти одну из них в Рекомендации по программному обеспечению StackExchange:

    Современная библиотека C ++ CSV Reader (и, возможно, писатель)

2 голосов
/ 20 февраля 2020

В то время как l oop, вы никогда не помещали никаких значений в ваш вектор.

Похоже, у вас есть все, что вам нужно, чтобы прочитать CSV в вектор прямо здесь. Единственная проблема в том, что вы остановились на именах столбцов.

// Read the column names
    if(fin.good())
    {
        std::getline(fin, line);
        std::stringstream ss(line);
        while(std::getline(ss, colname, ',')){
            result.push_back(colname);
            cout << colname << endl;
        }
    }

Попробуйте изменить код, который я скопировал выше, на:

// Read the column names
    while(std::getline(fin, line))
    {
        std::getline(fin, line);
        std::stringstream ss(line);
        while(std::getline(ss, colname, ',')){
            result.push_back(colname);
            cout << colname << endl;
        }
    }
1 голос
/ 20 февраля 2020

Я не могу поверить, что мы используем библиотеку для такой очень простой вещи, как разбиение std::string на токены.

C ++ уже давно имеет встроенную и выделенную функциональность, специально разработанную для этого. цель - токенизировать строки (разбивать строки на токены). И поскольку такая простая выделенная функция, предназначенная для этой цели, доступна, ее просто следует использовать. Нет необходимости во внешних библиотеках или сложных конструкциях. Просто используйте std::sregex_token_iterator.

Это итератор (как и многие другие итераторы), который перебирает токены (подстроки) строки. Итак, что мы хотим.

Затем мы можем использовать конструктор диапазона std::vector s, чтобы написать что-то простое, например:

std::vector tokens(std::sregex_token_iterator(line.begin(), line.end(), delimiter, -1), {}));

Итак, мы определяем переменную с именем "tokens". "типа std::vector (с помощью CTAD тип вектора определяется автоматически). Мы используем его конструктор диапазона и предоставляем начальный и конечный итератор. Начальный итератор - это std::sregex_token_iterator, а конечный итератор - его инициализированный по умолчанию аналог.

Чтобы поместить такой вектор в двухмерный вектор, мы используем функцию внешних векторов emplace_back и делаем конструкцию на месте для внутреннего вектора.

Таким образом, вы читаете весь CSV-файл с 2 утверждениями

  • простой для l oop
  • простой переход с std::sregex_token_iterator
        // We will read all lines of the source file with a simple for loop and std::getline
        for (std::string line{}; std::getline(csvFile, line); ) {

            // We will split the one big string into tokens (sub-strings) and add it to our 2D array
            csvData.emplace_back(std::vector<std::string>(std::sregex_token_iterator(line.begin(), line.end(), delimiter, -1), {}));
        }

Итак, почему вы должны использовать библиотеку для такой простой задачи, которую вы можете выполнить с помощью 2 операторов? Я лично не понимаю этого. Таким образом, я считаю, что совет в принятом ответе совершенно неверный. Но, чтобы не начинать религиозные дискуссии: это мое очень личное скромное мнение, и каждый может делать то, что он хочет.

Пожалуйста, посмотрите полный рабочий пример, который решает вашу проблему, с помощью всего лишь нескольких строк кода. , .

#include <iostream>
#include <fstream>
#include <vector>
#include <regex>

const std::string csvFileName{ "r:\\csv.csv" };
const std::regex delimiter{ "," };

int main() {

    // Open the file and check, if it could be opened
    if (std::ifstream csvFile(csvFileName); csvFile) {

        // This is our "2D array string vector" as described in your post
        std::vector<std::vector<std::string>> csvData{};


        // Read the complete CSV FIle into a 2D vector ----------------------------------------------------
        // We will read all lines of the source file with a simple for loop and std::getline
        for (std::string line{}; std::getline(csvFile, line); ) {

            // We will split the one big string into tokens (sub-strings) and add it to our 2D array
            csvData.emplace_back(std::vector<std::string>(std::sregex_token_iterator(line.begin(), line.end(), delimiter, -1), {}));
        }
        // -------------------------------------------------------------------------------------------------


        // This is for summing up values
        double DP{}, Dta{}, Dts{};

        // Iterate in a simple for loop through all elements of the 2D vector, convert the vlaues to double and sum them up
        for (size_t i = 1U; i < csvData.size(); ++i) {

            DP += std::stod(csvData[i].at(1));
            Dta += std::stod(csvData[i].at(2));
            Dts += std::stod(csvData[i].at(3));
        }

        // Sho the result to the user
        std::cout << "\nSums:  DP: " << DP << "  Dta: " << Dta << "  Dts: " << Dts << "\n";
    }
    else { // In case that we could not open the source file
        std::cerr << "\n*** Error. Could not open file " << csvFileName << "\n\n";
    }
    return 0;
}

Но, как уже говорилось, каждый может делать все, что захочет.

...