0

I've been trying to read sequences of numbers from a file to a 2D vector without significant success.

Data in sequences.txt

1,2,3,4,5
6,3,1
7,4,10,4,1,9

I've encountered with few problems, reading sequences of numbers of an unknown length (format). For example if it was fixed a 3 number format, input would look like this:

input_stream >> integer >> char >> integer >> char >> integer

But the number of numbers of each sequence is unknown...

Another problem is the ',' char, it isn't located at the end of each sequence, meaning that somehow I would have go through the line right to the very last number in a sequence, then pick it up after I finish going through the line, is that possible?

My attempt of code:

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

void PrintMatrix(std::vector<std::vector<int>> matrix)
{
    for(auto v : matrix)
    {
        for(auto x : v)
        {
            std::cout << x << " ";
        }
        std::cout << std::endl;
    }
}

int main()
{
    std::fstream input_stream("sequences.txt", std::ios::in);
    if(!input_stream)
        std::cout << "Problem with opening a file";
    else
    {
        std::vector<std::vector<int>> matrix;
        while(input_stream.good())
        {
            std::vector<int> v;
            int number;
            char chr;
            while(input_stream >> number >> chr)
            {
                v.push_back(number);
            }
            matrix.push_back(v);
        }
        PrintMatrix(matrix);
    }
    return 0;
}

I started learning files very recently, I read most of the literature and now trying to practice, so any tips on how to solve this problem or any tips on working with files in general, I'd be very grateful :)

Thanks

P.S. Output that I get from this code (which is wrong): 1 2 3 4 5

Output I want to get:

1 2 3 4 5
6 3 1
7 4 10 4 1 9
  • Please take some time to read [Why is iostream::eof inside a loop condition considered wrong?](https://stackoverflow.com/questions/5605125/why-is-iostreameof-inside-a-loop-condition-considered-wrong) Using e.g. `input_stream.good()` is just the same and as bad. – Some programmer dude Jun 18 '17 at 13:03
  • Also reas up about passing using `const` and `references` - Would save a lot of copying for the print function – Ed Heal Jun 18 '17 at 13:05
  • As for a possible solution to your problem, I suggest you read whole lines into a string using [`std::getline`](http://en.cppreference.com/w/cpp/string/basic_string/getline). Then you can put that string into an [`std::istringstream`](http://en.cppreference.com/w/cpp/io/basic_istringstream) where you *again* can use `std::getline` using `','` as the separator to get each individual elements. – Some programmer dude Jun 18 '17 at 13:05
  • Thanks, I thought that good() method returns true as long as the stream is in a correct state. I will read more about it. Yes Ed Heal, I know about that, I just wanted to demonstrate my problem as simple as possible. Thanks for the tip Some programmer dude, I will try that – files_are_hard Jun 18 '17 at 13:10
  • Thanks to @Someprogrammerdude I have managed to solve my problem! https://pastebin.com/CWNz8KSj What do you think? Any suggestions to make the code work better? – files_are_hard Jun 18 '17 at 13:53

2 Answers2

0

Here is one possible solution, peek inside the loop where you read integers to determine whether you should move on to the next line

#include <iostream>
#include <vector>
#include <cassert>

using std::cin;
using std::cout;
using std::endl;

int main() {
    auto data = std::vector<std::vector<int>>(1);
    auto integer = int{};
    auto current_line = 0;
    while (cin >> integer) {
        data[current_line].push_back(integer);

        // read the next character and if it is a newline add a new entry in
        // the data vector
        auto next_character = char{};
        if (cin.get(next_character)) {
            if (next_character == '\n') {
                data.emplace_back();
                ++current_line;
            }
        } else {
            break;
        }
    }

    for (const auto& vec : data) {
        for (auto integer : vec) {
            cout << integer << " ";
        }
        cout << endl;
    }

    return 0;
}

Also as noted in the comments, one thing to keep in mind is that you should be using the istream itself to check for eof and termination of a loop rather than calling .good() .eof() and the like.

Curious
  • 19,352
  • 6
  • 45
  • 114
0

Here is another possible solution, using boost::split method for splitting the number in each of the lines.

#include <fstream>
#include <string>
#include <iostream>
#include <boost/algorithm/string.hpp>

void printMatrix(std::vector<std::vector<int> >& matrix)
{
    for(auto line : matrix)
    {
        for(auto num : line)
        {
            std::cout << num << " ";
        }
        std::cout << std::endl;
    }
}

int main()
{
    std::ifstream in;
    std::string line;
    in.open("sequences.txt");
    std::vector<std::vector<int> > mat;

    while(std::getline(in, line))
    {
        std::vector<std::string> text;
        std::vector<int> nums;
        boost::split(text, line, boost::is_any_of(","));
        for(auto num : text)
            nums.push_back(std::stoi(num));

        mat.push_back(nums);
    }
    printMatrix(mat);
}