Search code examples
c++arraysgetline

Read table of numbers into arrays when number of rows and columns determined at runtime


I would like to ask you about data input. I have a text file in the following format:

7 2

X Y

1 0
2 0.048922
3 0.0978829
4 0.146908
5 0.196019
6 0.245239
7 0.294584

The first line contains the number of rows and columns to be read in. The second line are headers. From the third line onwards it's only data. I would like to read my data into a 2D array (mat[][]) and the headers into an array of strings (title[]), which could be easily referenced later. I came this far writing the script. It can read the array numbers into the array, but not a 2D one. I was trying to declare a pointer for the matrix which is passed to the function but I couldn't do it. I also tried getline() but I do not know how to separate the headers \t delimited into separate strings.

#include <iostream>
#include <fstream>
#include <string>
#include <cmath>
using namespace std;

void readFile(char[]);

int main(){

    char fileName[] = "results.txt";

    readFile(fileName);
    return 0;
}


// Read file to matrix
void readFile(char fileName[]){

    int m, n;

    // Create streamobject
    ifstream infile;
    infile.open(fileName);

    // Exit if file opening failed
    if (!infile.is_open()){
        cerr<<"Opening failed"<<endl;
        exit(1);
    }

    // Get size of the matrix
    infile >> m >> n;

    // Pre-allocate memory
    string title;
    float *mat=new float[m*n];

    // Read title

    // Start reading data
    while (!infile.eof()){
        for(int i=0; i<m; i++){
            for(int j=0; j<n; j++){
                infile >> mat[i*n+j];
            }
        }
    }

    infile.close();
}

Could anyone help me out?


Thank you for the help so far. I will have a look at applying vectors this weekend, but I have to read up on it. I have quickly changed my script so now it does what I wanted: return a pointer to an array "mat" with the values. I have a function called 'showMatrix', which prints it to the screen. If I call showMatrix within the function 'readFile' it works fine. If on the other hand I call it outside from the main script then it returns completely wrong values.

I have a feeling there is something wrong here. Could you please help to point it out?

#include <iostream>
#include <fstream>
#include <string>
#include <cmath>
using namespace std;

void readFile(char[],float *mat,int &m,int &n);
void showMatrix(float *mat,int m,int n);


/*******************************************************************
 Script
 *******************************************************************/
int main(){

    char fileName[] = "results.txt";
    int m, n;
    float *mat;

    // Read data from file
    readFile(fileName,mat,m,n);
    showMatrix(mat,m,n);

    return 0;
}

// Read file to matrix
void readFile(char fileName[],float *mat,int &m,int &n){


    // Create streamobject
    ifstream infile;
    infile.open(fileName);

    // Exit if file opening failed
    if (!infile.is_open()){
        cerr<<"Opening failed"<<endl;
        exit(1);
    }

    // Get size of the matrix
    infile >> m >> n;

    // Pre-allocate memory
    mat=new float[m*n];

    // Read title
    std::string X;
    std::string Y;
    infile >> X >> Y;

    // Reading data
    while (!infile.eof()){
        for(int i=0; i<m; i++){
            for(int j=0; j<n; j++){
                infile >> mat[i*n+j];
            }
        }
    }
//    showMatrix(mat,m,n);
    infile.close();
}

// Print matrix to screen
void showMatrix(float *x,int m, int n){
    for(int i=0; i<m; i++){
        for(int j=0; j<n; j++){
            cout << x[i*n+j] << "\t";
        }
        cout << endl;
    }
}

Solution

  • Note doing this:

    while (!infile.eof()){
    

    Is nearly always wrong. If the file reading fails for any other reason than eof then you enter an infinite loop.

    Also the way you code is organized this will also mean that you try and initialize your data twice (though it will fail on the second pass an not do anything (apart from re-do the loop)) as the eof flag is not set until you read past the EOF and since you know how much data to read your first pass will not read past the EOF, you will read all the way up-to (but not past).

    Reading into a 1D array

    int main()
    {
        std::ifstream file("plop");
    
        int n;
        int m;
        std::string ColHeadX;
        std::string ColHeadY;
        // You forgot to read the Col Header out.
        // Just read them into strings.
        file >> n >> m >> ColHeadX >> ColHeadY;
    
        // Don't do manual memory management.
        // Use a vector it will do the work for you.
        std::vector<double>   data(n*m); // initialized with (n*m) elemensts
    
        // Always put the read in the loop test
        //      This bit:  file >> data[loop]
        // If this read fails you want the loop to exit immediately.
        // By putting the read here the result of the read will be
        // tested to see if it worked.
        for(int loop=0;loop < (n*m) && (file >> data[loop]); ++loop) { /*Empty*/}
    }
    

    Reading into a 2D array is nearly as simple:

    int main()
    {
        std::ifstream file("plop");
    
        int n;
        int m;
        std::string ColHeadX;
        std::string ColHeadY;
        // You forgot to read the Col Header out.
        // Just read them into strings.
        file >> n >> m >> ColHeadX >> ColHeadY;
    
        // Don't do manual memory management.
        // A vector of vectors gives you a 2D array.
        // The initialize(r) is slightly more complex but given the example above
        // I think you should be able to see the outer vector is initialized with
        // n copies of a vector with m elements. 
        std::vector<std::vector<double> >   data(n,std::vector<double>(m));
    
    
        // Again I would advise putting the read in the loop condition
        // The easiest way here is then to calculate the x/y coordinates in the body as you go.
        for(int loop=0, x=0, y=0;loop < (n*m) && (file >> data[x][y]);)
        {
           ++loop
           x = loop % n;
           y = loop / n;
        }
    
    }