简体   繁体   中英

how do I read a textfile into a 2D array in c++?

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;
}

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, eg

    /* 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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM