Thread: Issues with reading series of 2D matrices from file

  1. #1
    Registered User
    Join Date
    Mar 2016
    Posts
    203

    Issues with reading series of 2D matrices from file

    I'm facing some issues reading a series of 2D matrices from file into vector struct that I'd like to seek help on, please:
    1. ideally I'd like to use vector to store the matrices but when I declare vector<vector<int>> type data member in the struct below I get an error that SIZE is not a type . Outside the struct, in main() for eg exactly the same declaration works fine but not within struct
    Also when trying to use vector<vector<int>> from within the struct the overloaded insertion operation method gives an error: invalid types '<unresolved overloaded function type>
    2. following 1, I'm therefore using array[][] to store the matrices which appears to work within struct but only the first matrix is being read through fstream File. As shown, I'm also checking fail(), eof() for File after each read but neither seem to be the case when the program stops reading from .txt
    So, if someone could kindly advise where the code, as it stands, is incorrect and also how to use vector<vector<int>> as struct data member I'd be much obliged. Many thanks


    Code:
    #include<iostream>
    #include<vector>
    #include<fstream>
    #include<sstream>
    using namespace std;
    const size_t SIZE = 3;
    struct Matrices
    {
        int matrix[SIZE][SIZE];
        friend ostream& operator<< (ostream& os, const Matrices& m);
    };
    ostream& operator<< (ostream& os, const Matrices& m)
    {
    	for (size_t i = 0; i < SIZE;  i++)
        	{
            	for (size_t j = 0; j < SIZE; j++)
            	{
                            os<< m.matrix[i][j] <<"\t";
            	}
                	os<<"\n";
        	}
    	return os;
    }
    
    
    int main()
    {
    vector<Matrices> vec_mat;
    ifstream File("F:\\test.txt");
        	if(File)
        	{
                Matrices m;
                for (size_t i = 0; i < SIZE; i++)
                {
                    for (size_t j = 0; j < SIZE; j++)
                    {
                        File>> m.matrix[i][j];
                    }
                }
                vec_mat.emplace_back(m);
                if(File.eof()||File.fail()) cout<<"Fail \n";
            }
    
    
    	for (auto& itr : vec_mat)
    	{
    		cout<<itr<<"\n";
    	}
    }
    Sample .txt file:
    11 21 -3
    12 5 6
    -12 1 1
    2 3 4
    13 6 1
    1 2 2

  2. #2
    Registered User
    Join Date
    May 2010
    Posts
    4,632
    I'm facing some issues reading a series of 2D matrices from file into vector struct that I'd like to seek help on, please
    You need to decide if you want a vector of your structure/class that contains a single dimensional vector or if you want a single instance of your class that contains a vector<vector<int>>.

    By the way since you're using a structure that operator overload doesn't need to be a friend, it can be a "regular" non-friend function.

    Jim

  3. #3
    Registered User
    Join Date
    Mar 2016
    Posts
    203
    Jim: I wanted a single instance of the struct to have a vector<vector<int>> data-member (my attempt to do so is here: #10964387 - Pastie with the error messages commented in but it seems this vector<vector<int>> definition is ill-formed). And then I wanted to place the struct objects into a vector<struct> as each matrix was being read-in from File
    PS: removing the friend declaration gives error message 'ostream& Matrices operator<<(ostream&, const Matrices&)' must take exactly one argument

  4. #4
    Registered User
    Join Date
    Jun 2015
    Posts
    1,640
    How about:
    Code:
    struct Matrix {
        vector<vector<int>> m;
    
        Matrix(size_t rows, size_t cols) : m(rows) {
            for (size_t i = 0; i < rows; i++)
                m[i].resize(cols);
        }
    };
    And your code is only reading one matrix because there's no loop to read more than one!

    Naming issues:

    Matrices should be called Matrix since it only holds one.

    File should be called file since all other variables start with a lowercase letter. The common style is for user-defined types (like Matrix) to start with an uppercase letter and variables to start lowercase.

    itr is misnamed (if it stands for "iterator") since it is not an iterator. That's kind of the point of the new-style for - it allows you to avoid the actual iterators and deal just with the values.

    Code:
    #include <iostream>
    #include <vector>
    #include <fstream>
    using namespace std;
    
    const size_t SIZE = 3;
    
    struct Matrix {
        vector<vector<int>> m;
    
        Matrix(size_t rows, size_t cols) : m(rows) {
            for (size_t i = 0; i < rows; i++)
                m[i].resize(cols);
        }
    };
    
    ostream& operator<<(ostream& os, const Matrix& mat) {
        for (size_t i = 0; i < mat.m.size();  i++) {
            for (size_t j = 0; j < mat.m[i].size(); j++)
                os << mat.m[i][j] << '\t';
            os << '\n';
        }
        return os;
    }
    
    istream& operator>>(istream& is, Matrix& mat) {
        for (size_t i = 0; is && i < mat.m.size(); i++)
            for (size_t j = 0; is && j < mat.m[i].size(); j++)
                is >> mat.m[i][j];
        return is;
    } 
    
    int main() {
        vector<Matrix> mats;
        ifstream file("matrix_file");
        while (file) {
            Matrix m(SIZE, SIZE);
            file >> m;
            if (file)
                mats.push_back(move(m));
        }
        for (auto& mat : mats)
            cout << mat << '\n';
    }
    Note that it would be possible to read an arbitrarily-sized matrix by reading a line at a time, adding a row, reading those numbers (from an sstream) into the new row, and keep going until you encounter a blank line (or eof). So it could read a file like this:
    Code:
    1 2
    3 4
    
    8
    
    1 2 3 4 5 6 7
    7 6 5 4 3 2 1
    Last edited by algorism; 11-17-2016 at 10:31 AM.

  5. #5
    Registered User
    Join Date
    Mar 2016
    Posts
    203
    Thanks ever so much. Silly silly me, if(file) instead of while(file)!!!
    Some remaining questions however:
    - when the vector<vector<int>> is declared within struct why do we need to initialize it within the ctor and not as part of the declaration statment itself. For e.g. if I was declaring such within main() I could have written
    Code:
     vector<vector<int>> values(SIZE, vector<int>(SIZE));
    . Is it something to do with the fact that within struct values is no longer a stand-along variable but a data-member of a struct object? But then again, we can declare
    Code:
     vector<int> x;
    both in main() and as a struct data member
    - in the extraction operator overload why
    Code:
     is && i < ... ; is && j < ...;
    - the program seems to run even without the is && bits but are these meant as an extra layer of checks that the stream is still open?
    - what exactly does if(file), line 39, do? If I comment it out a third 3x3 matrix of 0's is printed in your program and a repeat of the 2nd matrix is printed in my array based program but I don't understand how a third matrix is generated without the if(file) expression
    Finally, thanks for your comments re naming styles. A couple of weeks ago Elysia also gave me some very valuable suggestions re indentation etc that I've tried to practice every since and I shall try and bear this in mind too

  6. #6
    Registered User
    Join Date
    Jun 2015
    Posts
    1,640
    - when the vector<vector<int>> is declared within struct why do we need to initialize it within the ctor
    The difference with your vector<int> x example is that it's using the default ctor. Try vector<int> v(10) and it won't work in the class. Here's how to call a ctor on an object in a class (in the ctor for the class):
    Code:
    AClass() : x(SIZE) {}
    So the our ctor could be written:
    Code:
    struct Matrix {
        vector<vector<int>> m;
        Matrix() : m(SIZE, vector<int>(SIZE)) {}
    };
    - in the extraction operator overload why is && ...
    It will basically work without those checks, but the loop will still spin SIZE*SIZE times, getting eof each time. I thought I'd short-circuit that.

    - what exactly does if(file), line 39, do?
    The end of file is detected in the statement file >> m. If it reads an entire matrix, then the eof hasn't been reached, file will be good and we have a good matrix. If file >> m detects eof, then file will be bad and we haven't read a (complete) matrix, so we don't push it.

    Here's the code with a couple of modifications (a simple use of templates and operator[] overloads) :
    Code:
    #include <iostream>
    #include <vector>
    #include <fstream>
    using namespace std;
     
    template<typename T>
    class Matrix {
        vector<vector<T>> m;
    public:
        Matrix(size_t rows, size_t cols) : m(rows, vector<T>(cols)) {}
        vector<T>& operator[](size_t i) { return m[i]; }
        const vector<T>& operator[](size_t i) const { return m[i]; }
        size_t size() const { return m.size(); }
    };
     
    template<typename T>
    ostream& operator<<(ostream& os, const Matrix<T>& mat) {
        for (size_t i = 0; i < mat.size();  i++) {
            for (size_t j = 0; j < mat[i].size(); j++)
                os << mat[i][j] << '\t';
            os << '\n';
        }
        return os;
    }
    
    template<typename T>
    istream& operator>>(istream& is, Matrix<T>& mat) {
        for (size_t i = 0; is && i < mat.size(); i++)
            for (size_t j = 0; is && j < mat[i].size(); j++)
                is >> mat[i][j];
        return is;
    } 
     
    int main() {
        vector<Matrix<int>> mats;
        ifstream file("matrix_file");
        if (!file) { cerr<<"error opening file\n"; return 1; }
        int rows = 0, cols = 0;
        file >> rows >> cols;
        while (file) {
            Matrix<int> m(rows, cols);
            file >> m;
            if (file)
                mats.push_back(move(m));
        }
        for (auto& mat : mats)
            cout << mat << '\n';
    }

  7. #7
    Registered User
    Join Date
    Mar 2016
    Posts
    203
    Thank you for your time and patience to explain my queries. Your template based illustration is particularly helpful. I notice most of the top-rated replies on stackoverflow etc generally start off with a template based approach before a specialization to the particular case and this looks like the way forward for my own development.
    Would you, or indeed anyone else, have reference(s) for when is it absolutely essential to call the ctor of a class member within the class ctor?
    Finally ...
    The end of file is detected in the statement file >> m. If it reads an entire matrix, then the eof hasn't been reached, file will be good and we have a good matrix. If file >> m detects eof, then file will be bad and we haven't read a (complete) matrix, so we don't push it.
    The way I'm explaining this back to myself is on the following lines: in our example with 2 matrices in the .txt file, when the second matrix has just been read the file pointer is immediately before eof(), so if(file) and hence push_back. Then we go back to while (file) which is true and so on to file >> m which reaches eof() and so if we don't have the if (file) here there is another push_back. Does this make sense?
    In any case with this example and my previous post (Reading .txt file across (blank) lines into struct) it seems once someone suggests I solution I try and come up with an explanation but it would be better if I could get to the solution myself instead of such post-rationalization. Once again, any tips or references re this general area would be much appreciated. Thanks

  8. #8
    Lurking whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    9,612
    Quote Originally Posted by sean_contab
    Would you, or indeed anyone else, have reference(s) for when is it absolutely essential to call the ctor of a class member within the class ctor?
    If your class has a base class, reference-type, or constant members, then these members must be initialized in a member initializer list, where you call other constructors. If you only wanted to use member initializer lists when you have to, that's basically what you need to know.

    Most people, I think, only choose to write as much as they can in the initializer list because they prefer the style. Yet another way to write the Matrix constructor:
    Code:
    template <typename T>
    Matrix::Matrix(size_t rows, size_t cols)
    {
       m.resize(rows, vector<T>(cols, T()));
    }
    Frankly, you don't need to write this because vector has a constructor that does this, too. Look at algorism's code for a reminder.

    Personally, I also choose to write my constructors with a member initializer list. It is often neat and complete, and I don't need to worry about too many C++ rules. You can't do everything in a member initializer list, but choosing to initialize members this way means that you never miss members that you have to do this way. The amount of code that you need to write in the body should be small--write a log, throw an exception, or (maybe) run a loop.

    If you're ever in doubt, your constructor is probably doing too much work.

    Quote Originally Posted by sean_contab
    Once again, any tips or references re this general area would be much appreciated. Thanks
    tl;dr -- look at your file, look at where the lines break and think about what the information might be. When you need to write code for an unknown formatted text file, look at it, and think like cin>> works.

    I want to get this out of the way -- if you are simply given a file and you have to figure out what all the data means by yourself, good luck. There is no shortcut for cleverness in such a case. The good news is that people often don't expect you to be clever. A good file has a format. A good format is explained somewhere, and it has some number of constraints. A constraint is, simply, a rule that data in the file won't break.

    You can write constraints in documentation and manuals. In fact, it will be hard to write documentation or manuals without writing down constraints, since you will essentially do it on accident just describing the file format. File formats in the real world, like the MP3, have a specification, which detail all you could ever want to know. There is nearly always something like a specification at hand, like your assignment sheet.

    A format can literally mean whatever is easiest to process. Take for example this file.

    4 4
    1 2 3 4
    8 7 6 5
    0 10 11 0
    0 3 4 2

    If you had to figure out what these numbers mean, the first, and only real hint there is, is where the lines break. This file is not only shaped like a matrix, but it also has a line on top of the matrix, which gives the bounds of the matrix.
    Last edited by whiteflags; 11-17-2016 at 11:18 PM. Reason: too many numbers! derp

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Sorting series inside a file
    By sean_cantab in forum C Programming
    Replies: 3
    Last Post: 04-22-2016, 11:32 AM
  2. issues reading and writing to file.
    By pack in forum C Programming
    Replies: 6
    Last Post: 03-20-2013, 11:11 AM
  3. issues reading a file.
    By Xinzin in forum C++ Programming
    Replies: 9
    Last Post: 08-17-2012, 07:53 PM
  4. issues reading from a file
    By sanddune008 in forum C Programming
    Replies: 3
    Last Post: 07-16-2010, 05:01 AM
  5. Question on reading matrices from file
    By chinesepirate in forum C Programming
    Replies: 1
    Last Post: 09-29-2008, 12:45 PM

Tags for this Thread