0

I am really struggling with trying to pass a comma separated integer textfile into a 2d array in c++. example, if the textfile looks like

2,4,5,6
3,7,5,3
4,8,4,2
6,7,3,0

how will I be able to put this into a 4 by 4 array please? I will later need to do calculations on it, which I know how to once it is in the 2d array. thank you in advance

so far I have

#include <iostream> 
#include <sstream>
#include <string>
#include <fstream>
using namespace std;
int main()
{

const int row =4;
const int col =4;
int array[row][col];
int r =0;
int c =0;
ifstream inputfile("numbers.txt");
if (!inputfile.is_open())
{cout<<"error"<<endl;
}
string line,num;
int number;
while(get line(inputfile,line))
{
string stream ss(line);
getline(ss,num,',');
number= stop(num);

for (int r=0; r<row;r++)
{
for (int c=0; c<col; c++)
{
array[row][col] =number;
}

}


inputfile.close();
return 0;
}
Shawn
  • 47,241
  • 3
  • 26
  • 60
  • 1
    Unless you add whatever code you have already, your question will most likely be closed. How far have you gotten? Can you open the file? Are you using `std::ifstream`? Etc. – 001 May 28 '20 at 19:44
  • Hey, thanks for the advice. I have updated my post to include my code. any assistance would be GREATLY appreciated. –  May 28 '20 at 20:39
  • @N7c Unfortunately you are probably too late as the question has already been closed. When asking questions on SO always show the code you are working on. It's the biggest reason for questions getting closed. Nothing to stop you asking a new question though. – john May 28 '20 at 20:47
  • @N7c Not sure why you think two for loops inside a while loop is the correct approach. To read into a 2D array you obviously only need two nested loops, not three. – john May 28 '20 at 20:48
  • Sorry, I am new to c++ (first year student). I thought it was to read from the text file until I reached the end of it. Thank you for the new knowledge though. My lecturers are currently kind people such as yourself on this site due to the quarantine LOL :) –  May 28 '20 at 20:53

1 Answers1

0

The good news is you have collected the right pieces of the puzzle to do what it is you are attempting to do. The bad news is ... you put the puzzle together wrong ... (but not terribly so).

The key is to systematically program each step you need to accomplish to read the csv file into a 2D array. (you should be using std::vector<std::vector<int>>, but I suspect the 2D array is an assignment requirement -- and it is also good to know how to handle arrays of fundamental types -- there are a lot out there in legacy code). Up through the point of reading each line from the file and populating the stringstream and then looking to parse with getline using the ',' as a delimiter everything looks okay.

What you are missing are protections to prevent reading more columns than you have storage for and more rows than you have declared. While this will not impact your read of the values from you file if your file contains exactly the number of values as you have declared for your array, it is vital for using arrays of fundamental types correctly. They are of fixed size and there is no auto-allocation that will save you if you attempt to write beyond the bounds of your array (you will just corrupt your program stack)

How do you protect your arrays bound? Simple, just check the the current row doesn't exceed the number allocated and the same for the columns, e.g.

    /* read each line, protect row bounds */
    while (r < row && getline (inputfile,line)) {
        int c = 0;      /* declare c local to loop so it is reset each iteration */
        std::string num;
        std::stringstream ss (line);
        /* loop reading from stringstream, protect column bounds */
        while (c < col && getline (ss, num, ',')) {
            try {   /* try/catch exception handling required for stoi conversion */
                array[r][c++] = std::stoi (num);    /* convert, increment col count */
            }
            catch (const std::exception & e) {
                std::cerr << "error: invalid conversion " << e.what() << '\n';
                return 1;
            }
        }
        if (c != col) { /* validate col number of values read & stored */
            std::cerr << "error: invalid number of columns '" << c << "' row: " 
                        << r << '\n';
            return 1;
        }
        r++;    /* increment row count */
    }

Also note the use of try/catch exception handling. When using std::stoi that is the only manner you have to validate the conversion, see cppreference.com - std::stoi and note the Exception heading detailing the two exception (std::invalid_argument and std::out_of_range) that must be handled. You cannot simply guess that all input files will be in the needed format with the correct values and hope for the best -- you must validate every input.

This applies to the number of columns in each row as well (and the number of rows filled when your read-loop is done). When you are done reading from the stringstring, you need to validate the number of column-values read is the number expected and that you have not encountered a short row. Note: these are the minimum-validations needed. You are free to write additional validations such as to check to ensure the stringstring is empty and that additional valued do not remain unread (not critical to the operation of the code, but may flag an invalidly formatted input file)

Lastly, while the remainder of the code simply outputs the values, take a look at Why is “using namespace std;” considered bad practice?. Develop good habits early. That also means Don't Hardcode Filenames. The arguments to main() provide a way to pass needed information into your program on startup -- use them, or at minimum, prompt for the filename to open.

Further, always compile with warnings enabled. That means -Wall -Wextra -pedantic -Wshadow for gcc/clang and for VS /W3. Including -Wshadow you would find that you shadow the previous declaration for variable c in for (int c=0; c<col; c++)

Putting it altogether, you could do something similar to:

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

#define ROW 4       /* while const int is fine, if you #define your constants  */
#define COL ROW     /* you have one single location at the top to make changes */

int main (int argc, char **argv) {

    const int row = ROW, col = COL; /* optional, you can just use ROW & COL */
    int array[row][col], r = 0;
    std::string line;

    if (argc < 2) { /* validate at least 1 argument given for filename */
        std::cerr << "error: insufficient arguments\nusage: ./prog filename\n";
        return 1;
    }

    std::ifstream inputfile (argv[1]);  /* open file provided as 1st argument */
    if (!inputfile.is_open()) { /* use std::cerr for error output, handle error */
        std::cerr << "error: file open failed '" << argv[1] << "'.\n";
        return 1;
    }

    /* read each line, protect row bounds */
    while (r < row && getline (inputfile,line)) {
        int c = 0;      /* declare c local to loop so it is reset each iteration */
        std::string num;
        std::stringstream ss (line);
        /* loop reading from stringstream, protect column bounds */
        while (c < col && getline (ss, num, ',')) {
            try {   /* try/catch exception handling required for stoi conversion */
                array[r][c++] = std::stoi (num);    /* convert, increment col count */
            }
            catch (const std::exception & e) {
                std::cerr << "error: invalid conversion " << e.what() << '\n';
                return 1;
            }
        }
        if (c != col) { /* validate col number of values read & stored */
            std::cerr << "error: invalid number of columns '" << c << "' row: " 
                        << r << '\n';
            return 1;
        }
        r++;    /* increment row count */
    }

    if (r < row) {  /* validate row number of arrays stored in 2D array */
        std::cerr << "error: invalid number of rows '" << r << "'\n";
        return 1;
    }

    for (r = 0; r < row; r++) {         /* loop outputting results */
        for (int c = 0; c < col; c++)
            std::cout << " " << array[r][c];
        std::cout << '\n';
    }
}

(note: the #define statements are optional, but provide a convenient place at the top of your code to adjust your constants if the need arises)

Example Use/Output

While your input file in dat/2darr.csv, you would execute and receive the following output:

$ ./bin/read2darrcsv dat/2darr.csv
 2 4 5 6
 3 7 5 3
 4 8 4 2
 6 7 3 0

Let me know if you have further questions.

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
  • Thank you so much David! Your in-depth explanation has given me the clarity that I previously lacked. This was extremely helpful and insightful. I will definitely be more aware of defensive programming. Greatly appreciate it. –  May 30 '20 at 17:06
  • 1
    You are very welcome. The key to programming, regardless of what language you use, is to ***validate, validate, validate***. That applies to every input, every conversion, ever critical step in your code where failure can occur -- you validate. The when, not if, something goes wrong, you know exactly where the failure occurred and why, and it is handled in an elegant manner. Good luck with your coding! – David C. Rankin May 31 '20 at 03:50