4

I'm working on an experiment that requires me to switch over to C++, which I'm still learning. I need to read data from a file into a 2D array, where the data in the file is composed of floating point numbers, laid out in a matrix format. However, each row of the matrix in the data file has a different number of columns, for example:

1.24 3.55 6.00 123.5
65.8 45.2 1.0
1.1 389.66 101.2 34.5 899.12 23.7 12.1

The good news is that I know the maximum number of possible rows/columns the file might have, and at least right now, I'm not particularly worried about optimization for memory. What I would like is to have a 2D array where the corresponding rows/columns match those of the file, and all the other elements are some known "dummy" value.

The idea I had was to loop through each element of the file (row by row), recognize the end of a line, and then begin reading the next line. Unfortunately I'm having trouble executing this. For example:

#include <iostream>
#include <fstream>

int main() {

const int max_rows = 100;
const int max_cols = 12;
//initialize the 2D array with a known dummy
float data[max_rows][max_cols] = {{-361}};

//prepare the file for reading
ifstream my_input_file;
my_input_file.open("file_name.dat");

int k1 = 0, k2 = 0; //the counters
while (!in.eof()) { //keep looping through until we reach the end of the file
    float data_point = in.get(); //get the current element from the file
    //somehow, recognize that we haven't reached the end of the line...?
        data[k1][k2] = next;
    //recognize that we have reached the end of the line
        //in this case, reset the counters
        k1 = 0;
        k2=k2+1;
}
}

And so I haven't been able to figure out the indexing. Part of the problem is that although I'm aware that the character "\n" marks the end of a line, it is of a different type compared to the floating point numbers in the file, and so I'm at a loss. Am I thinking about this the wrong way?

John Alperto
  • 137
  • 8
  • Do you have to use arrays? – KPCT Feb 17 '19 at 16:15
  • Is it impossible to accomplish with arrays? I don't have any experience with vectors... – John Alperto Feb 17 '19 at 17:56
  • *Is it impossible to accomplish with arrays* -- Not impossible, but impractical. If you look at your code, you came up with arbitrary sizes. Exceed those sizes in any way, that results in undefined behavior. Not use all the space, you have empty space not doing anything. Add to that, arrays are not resizable, so you can never have a "jagged" 2-d array. If anything, you're faking this by using 2-d arrays. In addition to all of that, you have no idea the "real size" of each of those rows when using a conventional array. Use `std::vector` -- it overcomes all of these issues. – PaulMcKenzie Feb 17 '19 at 18:15
  • `while (!in.eof())` -- and for this, [please read this](https://stackoverflow.com/questions/5605125/why-is-iostreameof-inside-a-loop-condition-considered-wrong) – PaulMcKenzie Feb 17 '19 at 18:20
  • @JohnAlperto see my updated answer!!!! I gave it my best to be as simple, as efficient and as understandable as possible!!!! – KPCT Feb 17 '19 at 21:44
  • @Gox Too bad it uses non-standard C++ (VLA's). There are too many newbie programmers using that syntax, believing they're writing valid C++ code (when they're not). – PaulMcKenzie Feb 17 '19 at 23:48
  • BTW, `float data[max_rows][max_cols] = {{-361}};` only initilized the first element in the first row to `-361`. The rest are zero (`0.0`). – Bo R Mar 14 '19 at 11:51
  • Yeah I realized that much later on, is there a way to initialize them all at once in C? Or do I need a for loop? – John Alperto Mar 14 '19 at 12:04

2 Answers2

3

You don't need to know the limits in advance if you stick to std::vector. Heres some example code that will read the file (assuming no non-floats in the file).

using Row = std::vector<float>;
using Array2D = std::vector<Row>;

int main() {
    Array2D data;
    std::ifstream in("file_name.dat");
    std::string line;
    Row::size_type max_cols = 0U;
    while (std::getline(in, line)) { // will stop at EOF
        Row newRow;
        std::istringstream iss(line);
        Row::value_type nr;
        while (iss >> nr) // will stop at end-of-line
            newRow.push_back(nr);
        max_cols = std::max(max_cols, newRow.size());
        data.push_back(std::move(newRow)); // using move to avoid copy
    }
    // make all columns same length, shorter filled with dummy value -361.0f
    for(auto & row : data)
        row.resize(max_cols, -361.0f);
}
Bo R
  • 1,899
  • 1
  • 5
  • 15
  • Appreciate your help. Can you explain what you're doing with "Row::size_type max_cols = 0U;" line? Also, as I'm unfamiliar with vectors, are push_back and resize unique to them? – John Alperto Feb 17 '19 at 18:04
  • The `Row::size_type ...` is just to get the correct type of the sizes used by the container in question, in this case, `std::vector` (it usually ends up as `std::size_t`. So instead I could have written `std::size_t max_cols = 0U;`. The `0U` is just my way of saying I want to assign an unsigned zero to the unsigned scalar type. `push_back` and `resize` are not unique to `vector`. See https://en.cppreference.com/w/cpp/container over methods that are shared over containers. On only reliable method is `.empty()`. – Bo R Feb 17 '19 at 18:11
1

Here is starting point, I will be working on code that you can use. Use two dimensional vector vector<vector<double>> and you can use getline() to get the line as a string than use string stream to get decimals from string.

And here is the code

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

int main (void) {
    std::vector<std::vector<double>> matrix;

    std::ifstream inputFile;
    inputFile.open("test.txt");
    char line[99];
    for (int i = 0; inputFile.getline(line, sizeof(line)); ++i) {    
        std::stringstream strStream (line);
        double val = 0.0;
        matrix.push_back(std::vector<double>());
        while (strStream >> val)
            matrix[i].push_back(val);
    }

    return 0;
}
KPCT
  • 3,815
  • 2
  • 16
  • 34
  • You need to fix the `char line[99]; for (int i = 0; inputFile.getline(line, 200); ++i)` part to match the size of the supplied buffer. Could I suggest `inputFile.getline(line,sizeof(line))` or `inputFile.getline(line, 99)`? – Bo R Feb 17 '19 at 17:29
  • `double vals[valCnt];` -- This is not valid C++. Why are you posting an answer that contains this, when everyone so far who has commented, as well as given an answer, stated to use `std::vector`? Maybe you should have stuck with just posting the `vector` example? – PaulMcKenzie Feb 17 '19 at 23:46
  • @PaulMcKenzie I understand your concerns, this is `C` style array but trust me that is how I was though. If you do not mind I will fix this mess bit later. :( Just came from outside my neighbor got stock. – KPCT Feb 18 '19 at 01:19
  • @PaulMcKenzie it is fixed now for good!!!! Sometimes it is more psychology than anything else when somebody is learning new thing. With me it is more of helping OP get the program to work the way he is comfortable than he can move on and experiment with `vectors`, `std::array` etc. – KPCT Feb 19 '19 at 15:58