c++c++11file-io2d-vector

How to put characters from file into two-dimensional vector?


I've been trying to read in characters from an external file to be put into a two-dimensional vector with type char. The elements must be able to be compared to certain values in order to navigate a maze given in "MazeSample.txt".

While I haven't been able to get characters be put into the vector, I was able to read and output the characters with the get and cout functions. The following code is an attempt to read the vectors in the correct format, but provides an error in the end:

//MazeSample.txt
SWWOW
OOOOW
WWWOW
WEOOW

//source.cpp
vector<vector<char>> maze;
ifstream mazeFile;
char token;

mazeFile.open("MazeSample.txt");

while (!mazeFile.eof()) {
    mazeFile.get(token); //reads a single character, goes to next char after loop

    for (int row = 0; row < maze.size(); row++) {
        for (int column = 0; column < maze.at(row).size(); row++) {
            maze.push_back(token);
        }
    }

    //cout << token;
}

mazeFile.close();

For the maze provided in "MazeSample.txt", I'd expect the maze vector to read each character row by row, mimicking the format of the maze sample.

In the above code, am provided with an error at maze.push_back(token): "no instance of overloaded function "std::vector<_Ty, _Alloc>::push_back..." matches the argument list" "argument types are: (char)" "object type is: std::vector>, std::allocator>>>"


Solution

  • The reason for your problem is that you try to put a char into a std::vector of std vector. So you put a wrong type.

    maze.at(row).push_back(token); would do it, but then no row exists. You also need to push_back and empty row, before you can write data to it.

    That is your syntax error.

    Then, your code could be drastically shortened by using C++ algorithms. See:

    
    #include <iostream>
    #include <vector>
    #include <algorithm>
    #include <iterator>
    #include <sstream>
    
    
    std::istringstream testDataFile(
    R"#(00000
    11111
    22222
    33333
    44444
    )#");
    
    
    
    // This is a proxy to read a complete line with the extractor operator
    struct CompleteLineAsVectorOfChar {
        // Overloaded Extractor Operator
        friend std::istream& operator>>(std::istream& is, CompleteLineAsVectorOfChar& cl) {
            std::string s{}; cl.completeLine.clear();  std::getline(is, s); 
            std::copy(s.begin(), s.end(), std::back_inserter(cl.completeLine));
            return is; }
    
        operator std::vector<char>() const { return completeLine; }  // Type cast operator for expected value
        std::vector<char> completeLine{};
    };
    
    
    int main()
    {
        // Read complete source file into maze, by simply defining the variable and using the range constructor
        std::vector<std::vector<char>> maze { std::istream_iterator<CompleteLineAsVectorOfChar>(testDataFile), std::istream_iterator<CompleteLineAsVectorOfChar>() };
    
        // Debug output:  Copy all data to std::cout
        std::for_each(maze.begin(), maze.end(), [](const std::vector<char> & l) {std::copy(l.begin(), l.end(), std::ostream_iterator<char>(std::cout, " ")); std::cout << '\n'; });
    
        return 0;
    }
    

    But this is not the end. A std::vector<char>has no advantage over a string. You can work nearly have all the same functionality as a std::vector<char>. That is an improvement in design. The code would then look more like this:

    #include <iostream>
    #include <vector>
    #include <algorithm>
    #include <iterator>
    #include <sstream>
    
    std::istringstream testDataFile(
    R"#(00000
    11111
    22222
    33333
    44444
    )#");
    
    int main()
    {
        // Read complete source file into maze, by simply defining the variable and using the range constructor
        std::vector<std::string> maze{ std::istream_iterator<std::string>(testDataFile), std::istream_iterator<std::string>() };
    
        // Debug output:  Copy all data to std::cout
        std::copy(maze.begin(), maze.end(), std::ostream_iterator<std::string>(std::cout, "\n"));
    
        return 0;
    }
    

    This is the by far more simpler solution. And it will serve your needs as well.

    Please note: I used a istringstream for reading data, becuase I do not have a file on SO. But it is of cause the same as using any other stream (like an ifstream).

    EDIT

    The first solution read the source and put it directly into a std::vector<std::vector<char>>:

    The 2nd solution put everything in the a std::vector<std::vector<std::string>> which is the most efficient solution. Also a std::string is nearly a std::vector<std::vector<char>>.

    The OP requested a 3rd solution where we use the 2nd solution and then copy the std::vector<std::vector<std::string>> into a std::vector<std::vector<char>>.

    Please see below

    
    #include <iostream>
    #include <vector>
    #include <algorithm>
    #include <iterator>
    #include <sstream>
    
    std::istringstream testDataFile(
        R"#(00000
    11111
    22222
    33333
    44444
    )#");
    
    int main()
    {
        // Read complete source file into maze, by simply defining the variable and using the range constructor
        std::vector<std::string> maze{ std::istream_iterator<std::string>(testDataFile), std::istream_iterator<std::string>() };
    
        // Debug output:  Copy all data to std::cout
        std::copy(maze.begin(), maze.end(), std::ostream_iterator<std::string>(std::cout, "\n"));
    
    
        // Edit: Copy into a std::vector<std::vector<char> -------------------------------------------------------
        std::cout << "\n\n\nSolution 3:\n\n";
    
        // Define the new variable with number of lines from the first maze
        std::vector<std::vector<char>> mazeChar(maze.size());
    
        // Copy the data from the original maze
        std::transform(
            maze.begin(),               // Source
            maze.end(), 
            mazeChar.begin(),           // Destination
            [](const std::string & s) {
                std::vector<char>vc;    // Copy columns
                std::copy(s.begin(), s.end(), std::back_inserter(vc)); 
                return vc; 
            }
        );
    
        // Debug Output
        std::for_each(
            mazeChar.begin(),
            mazeChar.end(),
            [](const std::vector<char> & vc) {
                std::copy(vc.begin(), vc.end(), std::ostream_iterator<char>(std::cout));
                std::cout << '\n';
            }
        );
    
        return 0;
    }
    

    Hope this helps . . .