How to read file into vector of tuple?

Viewed 395

I would like to read a text file, which contains the following data:

Wogger John 2   6.2
Bilbo   111 81.3
Mary    29  154.8

The data separated by tabulator. The problem is if the string contains a space (for example: 'Wogger John'), then the program doesn't work. THe program does work if I replace string 'Wogger John' to 'Wogger' or 'John'. How to fix the problem? How to use the getline() function. Here the code:

#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <cerrno>
#include <cstring>
#include <tuple>

std::vector<std::tuple<std::string, int, double>> Readfile()
{
    std::ifstream File("file_read_v3.txt");
    std::vector<std::tuple<std::string, int, double>> data;
    std::string name;
    int a;
    double b;

    while (File >> name >> a >> b)
    {
        
        data.push_back(std::tuple<std::string, int, double>(name, a, b));
    }
    return data;
}

int main()
{
    auto vt = Readfile();
    
    for (const auto& i : vt) {
        std::cout << std::get<0>(i) << ", " << std::get<1>(i) << ", " << std::get<2>(i) << std::endl;
    }
    
    return 0;
    system("pause");
}
3 Answers
while (std::getline(File, name, '\t') >> a >> b)
{        
    data.push_back(std::tuple<std::string, int, double>(name, a, b));
}

std::getline reads up to and including a delimiter, then discards the delimiter. It defaults to the delimiter being a newline; here we make it a tab.

If the separator was always the same, and different from the separator between the names you could use it in getline as a delimiter, something like:

while (std::getline(File, name, '\t')){
    File >> a >> b;
    data.push_back(std::tuple<std::string, int, double>(name, a, b));          
}

But by your sample file contents, it seems that you have tabs and spaces randomly placed, for that reason getline wouldn't really help very much, the problem remains.

You could, for instance, read the file char by char and stop reading when you encouter a digit, put that digit back and then parse the numeric values, roughly something like this:

std::vector<std::tuple<std::string, int, double>> Readfile()
{
    std::ifstream File("file_read_v3.txt");
    std::vector<std::tuple<std::string, int, double>> data;

    if (File.is_open()) // always check if file was opened
    {
        int a;
        double b;
        char c;

        do
        {
            std::string name;
            while (!std::isdigit(c = File.get())) // get character, check if is digit
            {
                name.push_back(c); // add characters until a digit is found
            }
            File.unget(); // put digit back
            File >> a >> b; // parse the int and double
            data.push_back(std::tuple<std::string, int, double>(name, a, b));
            c = File.get(); // get next character
        } while (c != EOF); // if there is nothing else to read
    }
    return data;
}

To do, remove the space or tab at the end of name.

Thise line:

while (File >> name >> a >> b)

is causing your problems. You are saying cut my line into 1 string and 2 ints seperated by whitespace, so the line:

Wogger John 2   6.2

can not be parsed because it contains 2 strings + 2 ints and the stream operator is going to return false and your while loop is exiting.

To solve this natively you need to parse every char until you hit an \t. Put the parsed chars in a string, then parse the number until \t comes again and parse the next number.

Related