C ++ Использование getline () внутри цикла для чтения в файл CSV - PullRequest
0 голосов
/ 30 июня 2019

Я пытаюсь прочитать в файле CSV, который содержит строки из 3 человек / пациентов, где столбец 1 - это идентификатор пользователя, столбец 2 - это имя, столбец 3 - это имя, столбец 4 - это страховка, а столбец 5 - это версия, которая выглядит что-то вроде ниже.

Редактировать: Извинения, я просто скопировал / вставил мою таблицу CSV сюда, чтобы раньше она не показывала запятые. Разве это не будет выглядеть как-то ниже? Джон ниже также указал, что после версии нет запятых, и это, похоже, решило проблему! Большое спасибо, Джон! (пытаясь понять, как я могу принять ваш ответ :))

nm92,Nate,Matthews,Aetna,1
sc91,Steve,Combs,Cigna,2
ml94,Morgan,Lands,BCBS,3

Я пытаюсь использовать getline () внутри цикла для чтения всего, и он отлично работает для первой итерации, но getline (), кажется, заставляет его пропустить значение на следующих итерациях. Любая идея, как я могу решить это?

Я также не уверен, почему вывод выглядит следующим образом, потому что я не вижу, где в коде печатаются строки w / "sc91" и "ml94". Вот как выглядит вывод текущего кода.

userid is: nm92
fname is: Nate
lname is: Matthews
insurance is: Aetna
version is: 1
sc91
userid is: Steve
fname is: Combs
lname is: Cigna
insurance is: 2
ml94
version is: Morgan
userid is: Lands
fname is: BCBS
lname is: 3

insurance is:
version is:

Я провел множество исследований различий между getline () и оператором >> stream, но большинство материалов getline (), похоже, вращаются вокруг получения ввода из cin, а не чтения из файла, как здесь, поэтому Я думаю, что что-то происходит с / getline () и как он читает файл, что я не понимаю. К сожалению, когда я попробовал >> operator, это заставляет меня использовать функцию strtok (), и я много боролся со строками c и присваивал их массиву строк C ++.

#include <iostream>
#include <string>                               // for strings
#include <cstring>                              // for strtok()
#include <fstream>                              // for file streams

using namespace std;

struct enrollee
{
    string userid = "";
    string fname = "";
    string lname = "";
    string insurance = "";
    string version = "";
};

int main()
{
    const int ENROLL_SIZE = 1000;               // used const instead of #define since the performance diff is negligible,
    const int numCols = 5;                    // while const allows for greater utility/debugging bc it is known to the compiler ,
                                                // while #define is a preprocessor directive
    ifstream inputFile;                         // create input file stream for reading only
    struct enrollee enrollArray[ENROLL_SIZE];   // array of structs to store each enrollee and their respective data
    int arrayPos = 0;

    // open the input file to read
    inputFile.open("input.csv");
    // read the file until we reach the end
    while(!inputFile.eof())
    {
        //string inputBuffer;                         // buffer to store input, which will hold an entire excel row w/ cells delimited by commas
                                                    // must be a c string since strtok() only takes c string as input
        string tokensArray[numCols];
        string userid = "";
        string fname = "";
        string lname = "";
        string insurance = "";
        string sversion = "";
        //int version = -1;

        //getline(inputFile,inputBuffer,',');
        //cout << inputBuffer << endl;

        getline(inputFile,userid,',');
        getline(inputFile,fname,',');
        getline(inputFile,lname,',');
        getline(inputFile,insurance,',');
        getline(inputFile,sversion,',');

        enrollArray[0].userid = userid;
        enrollArray[0].fname = fname;
        enrollArray[0].lname = lname;
        enrollArray[0].insurance = insurance;
        enrollArray[0].version = sversion;

        cout << "userid is: " << enrollArray[0].userid << endl;
        cout << "fname is: " << enrollArray[0].fname << endl;
        cout << "lname is: " << enrollArray[0].lname << endl;
        cout << "insurance is: " << enrollArray[0].insurance << endl;
        cout << "version is: " << enrollArray[0].version << endl;
    }
}

Ответы [ 3 ]

1 голос
/ 30 июня 2019

Ваша проблема в том, что после последнего элемента данных в каждой строке нет запятой, поэтому

 getline(inputFile,sversion,',');

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

Чтобы это исправить, просто замените код выше на

 getline(inputFile,sversion);

, который будет читать до конца строки, как требуется.

1 голос
/ 30 июня 2019

Относительно вашей функции. Если вы посмотрите на структуру исходного файла, то увидите, что он содержит 5 строк, разделенных ",". Таким образом, типичный файл CSV.

При вызове std::getline будет прочитана полная строка с 5 строками. В своем коде вы пытаетесь вызвать std::getline для каждой отдельной строки, после которой следует запятая. Commaa отсутствует после последней строки. Это не будет работать. Вы также должны использовать getline, чтобы получить полную строку.

Вам нужно прочитать всю строку и затем токенизировать ее.

Я покажу вам пример того, как это сделать с std::sregex_token_iterator. Это очень просто. Кроме того, мы перезапишем оператор вставки и экстракота. При этом вы можете легко читать и записывать данные "enrollee", например Enrollee e{}; std::cout << e;

Кроме того, я использую алгоритмы C ++. Это делает жизнь очень легкой. Вход и выход являются однострочными в основном.

Пожалуйста, смотрите:

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


struct Enrollee
{
    // Data
    std::string userid{};
    std::string fname{};
    std::string lname{};
    std::string insurance{};
    std::string version{};

    // Overload Extractor Operator to read data from somewhere
    friend std::istream& operator >> (std::istream &is, Enrollee& e) {
        std::vector<std::string> wordsInLine{};       // Here we will store all words that we read in onle line;
        std::string wholeLine;                        // Temporary storage for the complete line that we will get by getline
        std::regex separator("[ \\;\\,]"); ;          // Separator for a CSV file
        std::getline(is, wholeLine);                  // Read one complete line and split it into parts
        std::copy(std::sregex_token_iterator(wholeLine.begin(), wholeLine.end(), separator, -1), std::sregex_token_iterator(), std::back_inserter(wordsInLine));
        // If we have read all expted strings, then store them in our struct
        if (wordsInLine.size() == 5) {
            e.userid = wordsInLine[0];
            e.fname = wordsInLine[1];
            e.lname = wordsInLine[2];
            e.insurance = wordsInLine[3];
            e.version = wordsInLine[4];
        }
        return is;
    }

    // Overload Inserter operator. Insert data into output stream
    friend std::ostream& operator << (std::ostream& os, const Enrollee& e) {
        return os << "userid is:    " << e.userid << "\nfname is:     " << e.fname << "\nlname is:     " << e.lname << "\ninsurance is: " << e.insurance << "\nversion is:   " << e.version << '\n';
    }
};


int main()
{
    // Her we will store all Enrollee data in a dynamic growing vector
    std::vector<Enrollee> enrollmentData{};

    // Define inputFileStream and open the csv
    std::ifstream inputFileStream("r:\\input.csv");

    // If we could open the file
    if (inputFileStream) {

        // Then read all csv data
        std::copy(std::istream_iterator<Enrollee>(inputFileStream), std::istream_iterator<Enrollee>(), std::back_inserter(enrollmentData));

        // For Debug Purposes: Print all data to cout
        std::copy(enrollmentData.begin(), enrollmentData.end(), std::ostream_iterator<Enrollee>(std::cout, "\n"));
    }
    else {
        std::cerr << "Could not open file 'input.csv'\n";
    }
}

Это будет читать входной файл "input.csv", содержащий

nm92,Nate,Matthews,Aetna,1
sc91,Steve,Combs,Cigna,2
ml94,Morgan,Lands,BCBS,3

И показать как вывод:

userid is:    nm92
fname is:     Nate
lname is:     Matthews
insurance is: Aetna
version is:   1

userid is:    sc91
fname is:     Steve
lname is:     Combs
insurance is: Cigna
version is:   2

userid is:    ml94
fname is:     Morgan
lname is:     Lands
insurance is: BCBS
version is:   3
1 голос
/ 30 июня 2019

Это всего лишь идея, но она может вам помочь. Это кусок кода одного проекта, над которым я работаю:

std::vector<std::string> ARDatabase::split(const std::string& line, char delimiter)
{
    std::vector<std::string> tokens;
    std::string token;
    std::istringstream tokenStream(line);
    while (std::getline(tokenStream, token, delimiter))
    {
        tokens.push_back(token);
    }
    return tokens;
}

void ARDatabase::read_csv_map(std::string root_csv_map)
{
    qDebug() << "Starting to read the people database...";
    std::ifstream file(root_csv_map);
    std::string str;
    while (std::getline(file, str))
    {
        std::vector<std::string> tokens = split(str, ' ');
        std::vector<std::string> splitnames = split(tokens.at(1), '_');

        std::string name_w_spaces;
        for(auto i: splitnames) name_w_spaces = name_w_spaces + i + " ";

        people_names.insert(std::make_pair(stoi(tokens.at(0)), name_w_spaces));
        people_images.insert(std::make_pair(stoi(tokens.at(0)), std::string("database/images/" + tokens.at(2))));

    }
}

Вместо std :: vector вы можете использовать другой контейнер, более подходящий для вашего случая. И последний пример сделан для формата ввода моего случая. Вы можете легко изменить его для адаптации к вашему коду.

...