0

I want to extract data from a csv file but I have to get the number of rows and columns of the table first.

What I have so far is the following:

        std::ifstream myfile(filename); // filename is a string and passed in by the constructor
        if (myfile.is_open())
        {
            // First step: Get number of rows and columns of the matrix to initialize it.
            // We have to close and re-open the file each time we want to work with it.
            int rows = getRows(myfile);
            std::ifstream myfile1(filename);
            int columns = getColumns(myfile1);

            if (rows == columns) // Matrix has to be quadratic.
            {
                std::ifstream myfile2(filename);
                abwicklungsdreieck.set_Matrix(QuantLib::Matrix(rows, columns, 0)); // abwicklungsdreieck is initialised before
                //...
            }
            else
            {
                std::cout << "\nNumber of rows has to equal number of columns.";
            }
        }
    // [...]
    int getRows(std::ifstream &myfile)
    {
        std::string line;
        int rows = 0;

        while (std::getline(myfile, line)) // While-loop simply counts rows.
        {
            rows++;
        }
        myfile.close();
        return rows - 1;
    }

    int getColumns(std::ifstream &myfile)
    {
        std::string line;
        char delimiter = ';';
        size_t pos = 0;
        int columns = 0;

        while (std::getline(myfile, line) && columns == 0) // Consider first line in the .csv file.
        {
            line = line + ";";
            while ((pos = line.find(delimiter)) != std::string::npos) // Counts columns.
            {
                line.erase(0, pos + 1);
                columns++;
            }
        }
        myfile.close();
        return columns - 1;
    }

This code is working. However, I have to open the file for three times which I do not like. Is there a way to evade this?

I was thinking about working with tempfiles in getRows() and getColumns() but the copying streams isn't possible since it doesn't make sense as I learned recently.

So, is there another way do that? Or can I for example evade the getline() and the line.erase() methods?

  • 1
    why do you think you have to open the file three times? – 463035818_is_not_an_ai Nov 15 '17 at 17:31
  • 1
    maybe thats the source of your misunderstanding: You cannot copy a stream, but you can pass references around as you wish. Open it once, pass it around, do some stuff, close it once. – 463035818_is_not_an_ai Nov 15 '17 at 17:32
  • Yes but how do I pass it around? getLine(&myfile) doesn't work.. –  Nov 15 '17 at 17:39
  • you are actually already passing it by reference. No offense, but from your last comment I get the impression that you are missing some basics. I suggest you to take a look [here](https://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list) and do some research on references – 463035818_is_not_an_ai Nov 15 '17 at 17:41
  • Yes I do, that's why I'm asking here. Thought it was the most efficient way –  Nov 15 '17 at 17:42
  • sorry, but there is no way of learning C++ for dummies (still no offense). There is just too much opportunity to get it wrong. The only proper way to learn it is to rtfm – 463035818_is_not_an_ai Nov 15 '17 at 17:49

1 Answers1

0

You can read the file line by line, convert each line to a stream, then read the columns on the stream:

std::ifstream myfile(filename);
if(!myfile) return 0;

std::string line;
while(std::getline(myfile, line))
{
    std::stringstream ss(line);
    std::string column;
    while(std::getline(ss, column, ';'))
    {
        cout << column;
    }
    cout << "\n";
}

getline(myfile, line) will copy each row in to line.

Convert line to ss stream.

getline(ss, column, ';') will break the line in to columns.

Use std::stoi to convert the string in to integer.

If your matrix is based on std::vector, you can grow the vector one row at a time, so you don't need to know the size in advance.

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

void readfile(const std::string &filename)
{
    std::vector<std::vector<int>> matrix;
    std::ifstream myfile(filename);
    if(!myfile) return;
    std::string buf;
    while(std::getline(myfile, buf))
    {
        int maxrow = matrix.size();
        std::stringstream ss(buf);
        matrix.resize(maxrow + 1);
        cout << "break in to columns:" << buf << "\n";
        while(std::getline(ss, buf, ';'))
        {
            try {
                int num = std::stoi(buf);
                matrix[maxrow].push_back(num);
            }
            catch(...) { }
        }
    }

    for(auto &row : matrix) {
        for(auto col : row)
            cout << col << "|";
        cout << "\n";
    }
}
Barmak Shemirani
  • 30,904
  • 6
  • 40
  • 77
  • Thanks for your answer. I know that creating the matrix via vectors makes this easier, but I want to use QuantLib::Matrix since I need some functions of it in the further code. Thus I need to know in advance what the number of rows and columns are. After the setMatrix a function readFile(abwicklungsdreieck, myfile2) gets called which reads the file and sets the values out of it to the matrix of abwicklungsdreieck. –  Nov 16 '17 at 08:35