Sum of Multidimensional array in C++ from csv file - c++

Would anyone be able to help me, I am trying to make a C++ program which reads values from csv file and prints them(there are going to be three 4 rows, 3 columns). I want to know how can I add the rows (like the sum of the first row =? the sum of second-row = ?...)
The matrix looks like this:
And my program looks like:
#include <iostream>
#include <fstream>
using namespace std;
int main() {
ifstream sumCol;
sumCol.open("Col.csv");
string line;
int sum;
cout << "The entered matrix: " << endl;
while(sumCol.good()){
string line;
getline(sumCol, line, ',');
cout << line << " ";
}
while(sumCol.good()){
getline(sumCol, line, ',');
int i = stoi(line);
cout << endl;
cout << i;
}
while(getline(sumCol, line, ',')){
int i = stoi(line);
sum = sum + i;
getline(sumCol, line, ',');
i = stoi(line);
sum = sum + i;
getline(sumCol, line);
i = stoi(line);
sum = sum + i;
}
cout << sum << endl;
return 0;
}

Next try. Very simple answer with just basic constructs.
12 statements. But 2 for loops, nested and hardcoded magic numbers for rows and columns.
Please see the below well commented source code:
#include <iostream>
#include <fstream>
int main() {
// Open file
std::ifstream csv("col.csv");
// and check, if it could be opened
if (csv) {
// Now the file is open. We have 3 rows and 4 columns
// We will use 2 nested for loops, one for the rows and one for the columns
// So, first the rows
for (int row = 0; row < 3; ++row) {
// Every row has a sum
int sumForOneRow = 0;
// And every row has 4 columns. Go through all coulmns of the current row.
for (int col = 0; col < 4; ++col) {
// Read an integer value from the current line
int integerValue;
csv >> integerValue;
// Show it on the screen
std::cout << integerValue << ' ';
// Update the sum of the row
sumForOneRow = sumForOneRow + integerValue;
}
// Now, the inner for loop for the 4 columns is done. Show sum to user
std::cout << " --> " << sumForOneRow << '\n';
// Line activities are done now for this line. Go on with next line
}
}
else std::cerr << "\n*** Error: Could not open 'col.csv'\n";
return 0;
}

Third try.
Obviously the matrix, shown in the question, does not reflect the real data. The real data might look like that:
9, 1, 2, 4
9, 2, 8, 0
3, 3, 3, 3
Without using std::getline we can do like the below:
#include <iostream>
#include <fstream>
int main() {
// Open file
std::ifstream csv("r:\\col.csv");
// and check, if it could be opened
if (csv) {
// Now the file is open. We have 3 rows and 4 columns
// We will use 2 nested for loops, one for the rows and one for the columns
// So, first the rows
for (int row = 0; row < 3; ++row) {
// Every row has a sum
int sumForOneRow = 0;
// And every row has 4 columns. Go through all coulmns of the current row.
for (int col = 0; col < 4; ++col) {
// Read an integer value from the current line
int integerValue;
char c; // for the comma
// The last value, the value in column 3 (We start counting with 0) is not followed by a comma
// Therefore we add special treatment for the last column
if (col == 3)
csv >> integerValue; // Read just the value
else
csv >> integerValue >> c; // Read value and comma
// Show it on the screen
std::cout << integerValue << ' ';
// Update the sum of the row
sumForOneRow = sumForOneRow + integerValue;
}
// Now, the inner for loop for the 4 columns is done. Show sum to user
std::cout << " --> " << sumForOneRow << '\n';
// Line activities are done now for this line. Go on with next line
}
}
else std::cerr << "\n*** Error: Could not open 'col.csv'\n";
return 0;
}

4th try. Based on the evolution of this thread and the request of the OP to use std::getline
#include <iostream>
#include <fstream>
#include <string>
int main() {
// Open file
std::ifstream csv("r:\\col.csv");
// and check, if it could be opened
if (csv) {
// Now the file is open. We have 3 rows and 4 columns
// We will use 2 nested for loops, one for the rows and one for the columns
// So, first the rows
for (int row = 0; row < 3; ++row) {
// Every row has a sum
int sumForOneRow = 0;
// And every row has 4 columns. Go through all coulmns of the current row.
for (int col = 0; col < 4; ++col) {
// Read a substring up to the next comma or end of line
std::string line;
// Special handling for last column. This is not followed by a comma
if (col == 3)
std::getline(csv, line);
else
std::getline(csv, line, ',');
// Convert string to line
int integerValue = std::stoi(line);
// Show it on the screen
std::cout << integerValue << ' ';
// Update the sum of the row
sumForOneRow = sumForOneRow + integerValue;
}
// Now, the inner for loop for the 4 columns is done. Show sum to user
std::cout << " --> " << sumForOneRow << '\n';
// Line activities are done now for this line. Go on with next line
}
}
else std::cerr << "\n*** Error: Could not open 'col.csv'\n";
return 0;
}
IMHO this is more complicated than 3rd try

Yes, I can help you with a piece of code, but I am not sure, if you will understand the more modern C++ language elements.
So, what do we need to do?
Read line by line of the source csv string
Get all integer values for this line
The interger values are arranged in columns and separated by white space
Show the values and the screen
Sum up the values in onbe line and show the sum
And in the resulting code, we will do exactly that.
So, first, we open the file, and check, it it could be opened. Then, in a simple for loop, we read all lines that are present in the source code. Line by line.
In the body of the for loop, we take the current line that we just read, and put it into an std::istringstream. We do that to make extraction of the integer value simpler. And for the extraction, we use the std::istream_iterator. This will iterate over all integers in the line and return them.
We will store the values temporary in a std::vector. We use the range constructor of the std::vector. The begin iterator is the std::istream_iterator for the data type int and and for our std::istringstream. The end iterator is the default constructed std::istream_iterator, so, simply {}. This will copy all values into the std::vector
That is a powerful and compact on-liner.
We copy the values in the std::vector, so, the integer values of one line to the console.
Then, we add up all values in the std::vector (all integers from one line) and show the result on the screen.
And the beauty of it: It doesn't matter, how many rows and columns are present. It will work always. The only thing we need is space separated integer values in lines.
And all this can be done with just 7 statements . . .
Please see:
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
#include <algorithm>
#include <iterator>
#include <numeric>
int main() {
// Open file and check, if it could be opened
if (std::ifstream csv{ "col.csv" }; csv) {
// Read all lines from the source file
for (std::string line{}; std::getline(csv, line); ) {
// Put the current line in a stringstream for better extraction
std::istringstream iss{ line };
// Exctract all integer values from this line and put them int a vector
std::vector values(std::istream_iterator<int>(iss), {});
// Show values on display
std::copy(values.begin(), values.end(), std::ostream_iterator<int>(std::cout, " "));
// Claculate the sum for one line on show on display.
std::cout << " --> " << std::accumulate(values.begin(), values.end(), 0) << '\n';
}
}
else std::cerr << "\n*** Error: Could not open 'col.csv'\n";
return 0;
}
Language is C++ 17

Related

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

C++ Basic While Loop: Unknown Inputs and Value Increment

So I'm trying to write a program that reads unknown inputs from a data file that has a sentinel 0 or I guess an termination point for the loop.
Number of integers per line before 0 (int count).
Number of all integers in data file (int totalcount).
Number of lines in data file (int lines).
Two examples of unknown inputs from a data file:
Example One:
1 2 3 0 4 5 6 7 0
Example Two:
0 9 11 -11
1 1 0 0 2
0
Here is my program (without "count" because that is where my problem lies):
int main()
{
//Declaring variables.
int input, lines, count, totalcount, datafile;
lines = 0;
count = 0;
totalcount = 0;
//Loop runs as long as data file has an integer to take in.
while(cin >> datafile)
{
//If datafile is not 0, loop runs until datafile is 0.
while(datafile != 0)
{
//Counts all integers in the file except 0.
totalcount += 1;
cin >> datafile;
}
//Counts how many lines there are in a data file using sentinel 0 (not "/n").
if(datafile == 0)
lines += 1;
//Outputs.
cout << lines << setw(11) << count << setw(11) << totalcount << endl;
}
return 0;
}
Please do not worry about technicality, efficiency, or anything else besides the logic/concept itself as I'm just trying to find the missing link in my knowledge to complete this assignment.
With that said, my expected outputs are as formatted:
"Line #" "Counts of integers per line" "Total counts of all integers in data file"
Using example one with my current code, I would have outputs (spacing is not exact and '.' is for blanks):
1......0......3
2......0......7
Correct expected outputs:
1......3......3
2......4......7
I would like any hints or explanation of how I can count the integers per line (before sentinel 0) and assign that value to "int count" without the value persisting to the next line.
I'm a student in an introductory C++ class so please show me a basic way of how I may go about this first and then any other advanced options as necessary for future applications.
Code of Conduct Personal Statement:
By participating, you are providing necessary knowledge for assignment completion, not completing the assignment itself. The example used is generated by me intended for concept demonstration purposes and is only a small part of the final assignment.
10/23/2016 9:56PM Update 1:
Currently attempting to use a "temp" variable to substract from "totalcount". I will update my code if attempt is successful.
totalcount is sum of counts. This is my suggestion
#include <iostream>
#include <iomanip>
using namespace std;
int main()
{
//Declaring variables.
int input, lines, count, totalcount, datafile;
lines = 0;
count = 0;
totalcount = 0;
//Loop runs as long as data file has an integer to take in.
while(cin >> datafile)
{
// Add count to totalcount and reset count each time reach 0
if (datafile == 0) {
totalcount += count;
lines += 1;
cout << lines << setw(11) << count << setw(11) << totalcount << endl;
count = 0;
}
//otherwise increase count
else {
count++;
}
}
return 0;
}

How to read in a matrix from a file in C++?

I am attempting to read in an n × n matrix from a file and then store that matrix in a one dimensional array. I would also like to store the value for n. I have looked into various approaches but can't seem to apply them to what I'm trying to achieve.
This is what I have so far but I'm unsure what to put into the while loop.
/*READ IN THE IMAGE MATRIX FROM THE FILE*/
String lineA;
ifstream imFile;
imFile.open("imageMatrixDefined.txt");
if(imFile.fail()){
cerr << "File cannot be found or opened" << endl;
exit(1);
}
if(imFile.is_open(){
cout << "file opened successfully!" << endl;
while(!imFile.eof()){
}
}
The input file could look like the following:
1 2 3
2 3 1
3 3 2
where a tab separates the elements.
Any suggestions would be greatly appreciated as I'm new to C++.
Arrays have a fixed size.
You have to get the value for n first before initializing an array.
It's good to double check if you would like to store that matrix in a one dimensional array or a two dimensional array. If it is a one dimensional array, checking how the matrix is stored in a one dimensional array is good. Some stores it first row, second row, ..., and nth row, and some stores it first column, second column, ..., and nth column.
The matrix is n × n, so the number of columns is equal to the number of rows.
The file stores one row at a time.
Getting the value of n is not difficult.
The while loop is critical, but before the while loop, getting the value of n is the first step of solving the problem.
When the value of n is gotten, the array is easy to be initialized.
You can read the line one by one from the file in the while loop, get each matrix element by using the delimiter as a tab character, and store the matrix element in the array.
Reading a 2D array to a file. Input is assumed to be comma separated, one row per line and x amount of columns per row, each column separated by a tab (\t) character. No EOL (end of line) tab's required, just a newline (\n). Remember to set rows and columns variables correctly. Code is tested to work correctly.
using namespace std;
#include <iostream>
#include <fstream>
int main() {
int rows=10, columns=10;
ifstream f("input.txt");
int input[rows][columns];
for (int i=0; i<rows; i++){
for (int j=0; j<columns; j++){
f >> input[i][j];
}
}
f.close();
// Here you can add processing of the file, for example, print it:
cout << "Input:" << endl;
for (int i=0; i<rows; i++){
for (int j=0; j<columns; j++){
cout << input[i][j] << " ";
}
cout << endl;
}
return 0;
}
You have to bind a custom input stream with your input file. Say your input file is "in.txt", and it looks the same as you have specified.
The following code then reads from the file, stores it in a 1D array and prints the resulting matrix in matrix form:
#include <iostream>
#include <fstream>
using namespace std;
int main () {
ifstream myfile;
myfile.open ("in.txt");
cout << "Reading from a file.\n";
int arr[10];
int k = 0;
for (int i = 0; i < 3; ++i)
{
for (int j = 0; j < 3; ++j)
{
myfile >> arr[k]; //Read it in a 1D array
cout << arr[k] << " ";
++k;
}
cout << "\n";
}
myfile.close();
return 0;
}
Output:
Reading from a file.
1 2 3
2 3 1
3 3 2

Writing sorted numbers from function out to text file?

I have two text files, each with an unknown number of integers sorted from lowest to highest... for example:
input file 1: 1 3 5 7 9 11...
input file 2: 2 4 6 8 10 ....
I want to take these numbers from both files, sort from low to high, and then output the full list of sorted numbers from both input files to a single output file. What I have so far...
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include "iosort.h"
int main()
{
const char* filename1 = "numberlist1.txt";
const char* filename2 = "numberlist2.txt";
std::ofstream ofs("output.txt");
std::ifstream ifs1, ifs2;
std::string input1, input2;
ifs1.open(filename1);
std::getline(ifs1, input1);
std::cout << "Contents of file 1: " << input1 << std::endl;
ifs2.open(filename2);
std::getline(ifs2, input2);
std::cout << "Contents of file 2: " << input2 << std::endl;
ioSort(ifs1, ifs2, ofs);
return 0;
}
and my function...
#include <fstream>
#include <sstream>
#include <vector>
#include "iosort.h"
void ioSort(std::ifstream& in1, std::ifstream& in2, std::ofstream& out)
{
int a, b;
std::vector<int> f1, f2, f3; //create one vector for each input stream
while (in1 >> a)
{
f1.push_back(a);
}
while (in2 >> b)
{
f2.push_back(b);
}
//now f1 and f2 are vectors that have the numbers from the input files
//we know that in these input files numbers are sorted from low to high
if (f1.size() > f2.size()) //input stream 1 was larger
{
for (int i = 0; i < f2.size(); i++)
{
if (f1[i] > f2[i]) //number at input vector 2 less that respective pos
{ //in input vector 1
f3.push_back(f2[i]);
}
else if(f1[i] == f2[i]) //numbers are equal
{
f3.push_back(f1[i]);
f3.push_back(f2[i]);
}
else //number in 1 is less than that in vector 2
{
f3.push_back(f1[i]);
}
}
for (int i = f2.size(); i < f1.size(); i++)
{
f3.push_back(f1[i]); //push remaining numbers from stream 1 into vector
}
}
else //input stream 2 was larger
{
for (int i = 0; i < f1.size(); i++)
{
if (f1[i] > f2[i]) //number at input vector 2 less that respective pos
{ //in input vector 1
f3.push_back(f2[i]);
}
else if(f1[i] == f2[i]) //numbers are equal
{
f3.push_back(f1[i]);
f3.push_back(f2[i]);
}
else //number in 1 is less than that in vector 2
{
f3.push_back(f1[i]);
}
}
for (int i = f1.size(); i < f2.size(); i++)
{
f3.push_back(f1[i]); //push remaining numbers from stream 2 into vector
}
}
//send vector contents to output file
for (int i = 0; i < f3.size(); i++)
{
out << f3[i] << " ";
}
}
Everytime I compile and run, the file output.txt is being created, but it is empty. Can anybody point me to what I am doing wrong. If, in main, I do something like:
out << 8 << " " << 9 << std::endl;
then it will show up in the output file.
AHA! Found your error. You're opening the file, then reading it directly to stdout (where you list the contents of your file), and then passing the same stream into your function. You cannot do this. Whenever you read from a file, the stream moves further through the file. By the time you're in your sorting function, you're at the end of the file, and so no numbers are read!
You need to remove the lines
std::getline(ifs1, input1);
std::cout << "Contents of file 1: " << input1 << std::endl;
and
std::getline(ifs2, input2);
std::cout << "Contents of file 2: " << input2 << std::endl;
Instead, print them out after you've stored them in the vector.
I'll leave the rest of my reply down below, since you, or posterity, might need it.
I'm not sure what's going on with your output file problem. Go through the whole chain and see where it's failing:
After you've read your file in, print out f1 and f2 with cout. Are they there and what you expect? If they are, we can move on.
After your algorithm has run, is your f3 there, and what you expect? If so, keep going!
This lets you diagnose the exact line where your code is failing (i.e. not doing it what you expect it do), and you know you can rule everything you've checked out.
Of course, instead of using cout you can launch this under a debugging environment and see what happens step by step, but if you don't know how, it'll take longer to do that to diagnose your problem the first time.
You do have other problems though, your merge function has errors. You end up skipping certain elements because you're only using one index for both arrays. Think about it: you only push one number into your output array in the f1[i] > f2[i] or the f1[i] < f2[i], but you discard both by incrementing i.
You can take your merge loop and simplify it by a lot, while also fixing your mistake :).
auto it = f1.cbegin();
auto jt = f2.cbegin();
while (it != f1.cend() && jt != f2.cend()) {
if (*it < *jt) f3.push_back(*jt++); //f2 was bigger, push f2>f3 and increment f2 index
else if (*it > *jt) f3.push_back(*it++); //f1 was bigger, push f1>f3 and increment f1 index
else { //implicit equals, only option left
f3.push_back(*jt++);
f3.push_back(*it++);
}
}
while (it != f1.cend()) f3.push_back(*it++);
while (jt != f2.cend()) f3.push_back(*jt++);
So now f3 contains your sorted array, sorted in O(m+n) time. If you're doing this for the sake of learning, I'd try to remedy your error using your way first before switching over to this.
If you want to write less code and speed isn't a problem, you can use <algorithm> to do this, too, but it's a terrible O((n+m)lg(n+m)).
auto it = f1.cbegin();
auto jt = f2.cbegin();
while (it != f1.cend()) f3.push_back(*it++);
while (jt != f2.cend()) f3.push_back(*jt++);
std::sort(f3.begin(), f3.end());
Since you're reading the file with std::getline() before calling ioSort(), there's nothing for the sorting function to read.
You can rewind back to the beginning of the file with seekg().
ifs1.clear();
ifs1.seekg(0, ifs1.beg);
ifs2.clear();
ifs2.seekg(0, ifs1.beg);
ioSort(ifs1, ifs2, ofs);
See How to read same file twice in a row
In order to be short:
#include <fstream>
#include <algorithm>
#include <iterator>
int main()
{
std::ifstream infile1("infile1.txt");
std::ifstream infile2("infile2.txt");
std::ofstream outfile("outfile.txt");
std::merge(
std::istream_iterator<int>{infile1}, std::istream_iterator<int>{},
std::istream_iterator<int>{infile2}, std::istream_iterator<int>{},
std::ostream_iterator<int>{outfile, " "}
);
}
std::merge is an STL algorithm that merge two sorted ranges into one sorted range. And the ranges are the files for this case. The files are viewed as ranges using std::istream_iterator<int>. The output file is accessed as a range using std::ostream_iterator<int>.

C++ text file into multi-dimensional array problems

As part of a school project, I would like to get an inventory *.txt file into an array in C++ and eventually back to a *.txt file at a later part in the program.
The text file will start out with 10 rows that will represent grocery story items and will include three columns that represent the name, price, and quantity of the items. I have been able to read from the file, even add numbers in front of each row that is displayed. Now, I would like to get the text file into a string array so that the "employee" user can make changes to items one at a time and then I can dump that array back into a *.txt file.
The code below is what I have been trying so far. I can get the count of rows in the file, but can't seem to get the columns counted or the data in the rows displayed. When I run the program, I get what appear to be 10 empty lines after it displays the rows (10) and Cols(0).
The columns in the *.txt file are normally separated by a space. I tried a tab, and tried: while(getline(invFile, lines, '\t'); which just caused the console to display what I am guessing was a memory address and then crashed.
Unfortunately, we have not gotten very far into debugging programs, and from the look of the syllabus, I don't think that will be covered very thoroughly, so I don't know how to troubleshoot any further. I have spent the last couple of hours Google-ing, and have gotten to the point that I actually need to ask for help.
The project involves a lot more than this component, but I really am stuck on this part. I am not asking for someone to do this for me, but if anyone has any idea what I am doing wrong and can point me in the best direction to get a text file into a multi-dimensional array, I would really appreciate it.
Thank you.
#include <iostream>
#include <fstream>
#include <iomanip>
#include <string>
#include <sstream>
#include <array>
int row = 0;
int col = 0;
using namespace std;
int main()
{
string lines;
int x;
string textArray[2][2];
ifstream invFile;
invFile.open("inventory.txt");
if(invFile.fail()){
cerr << "The file cannot be opened!";
exit(1);
}
cout << "\n" << endl;
while(invFile.good()) {
while(getline(invFile, lines)) {
istringstream streamA(lines);
col = 0;
while(streamA >> x) {
cout << x;
textArray[row][col] = x;
col++;
}
row++;
}
}
invFile.close();
cout << "Rows: " << row << endl;
cout << "Cols: " << col << endl;
cout << "\n" << endl;
for(int i=0; i<row; i++){
for(int j=0; j<col; j++){
cout << "Line: " << i << textArray[i][j] << ".";
}
cout << "\n";
}
return(0);
}
=============================
inventory.txt:
Apples 1.25 20
Oranges 1.75 20
Kiwi 2.50 15
Pineapples 5.50 20
Tomatoes 1.50 20
Onions 2.00 20
Corn 1.80 20
Carrots 2.30 20
Milk 4.50 20
Cheese 2.25 20
I would suggest that you create a struct or class to hold the data. From each line of text, extract the fields appropriately and them to your struct. Then, keep a list of those structs using std::vector.
#include <iostream>
#include <fstream>
#include <iomanip>
#include <string>
#include <sstream>
#include <array>
#include <vector>
#include <cstdlib>
using namespace std;
struct Row
{
vector<string> columns;
};
int main()
{
string line;
vector<Row> rows;
ifstream invFile;
invFile.open("inventory.txt");
if(invFile.fail()){
cerr << "The file cannot be opened!";
exit(1);
}
cout << "\n" << endl;
while(invFile.good()) {
while(getline(invFile, line))
{
Row row;
istringstream streamA(line);
string col;
while ( streamA >> col )
{
row.columns.push_back(col);
}
rows.push_back(row);
}
}
invFile.close();
cout << "Rows: " << rows.size() << endl;
cout << "Cols: " << rows[0].columns.size() << endl;
cout << "\n" << endl;
for(int i=0; i<rows.size(); i++){
for(int j=0; j<rows[i].columns.size(); j++){
cout << "Line: " << i << " " << rows[i].columns[j] << "\n";
}
cout << "\n";
}
return(0);
}
I'd like to suggest you add some print lines in the important step -- which I think also is a fast&good "debug" method. So that you can find where you wrong easily.
For example in your code, seems textArray wasn't assigned, so add some print nearby:
while(getline(invFile, lines)) {
cout <<"lines: " << lines << endl; //test enter here
istringstream streamA(lines);
col = 0;
while(streamA >> x) {
cout << "x is" << x; //test if enter here
textArray[row][col] = x;
col++;
}
row++;
}
Through the output, the lines is ok but cout << "x is" << x; wasn't printed, which means the while(streamA >>x) condition is false, why?
Go to find the library function called, std::istringstream x is int type but col 1 value is Apples, operator << will return NULL, it's unreasonable assing Apples to an int, till now, found point 1. If have to use int or float to store the numbers, use some convert API like atoi, atof.
After change x from int to string, got segmentation falut, it's obviously that textArray[2][2] is not enough to store all the information. "Out of range" is the reason of segmentation fault, so make a large array to test continue until passed.
There's a couple ways you could do this. The easiest would be to just put something like 3,10 at the top of the file, and then you know three columns and 10 rows. Since your writing this after modification, you would just need to make sure that those numbers get written correctly.
If you want to learn some more advanced methods, then your life will be easier AFTER you learn a bunch more.
If you used a vector, using something like vector< vector<string> > you could just read to a stringstream and then split the line read and put it into the vector
fstream file(...);
string tempString;
vector< vector<string> > list;
// Get a full line
while(getline(file, tempString, '\n'){
// Create a StringStream and store tempString in it for further manipulation
stringstream ss;
ss << tempString;
vector<string> tempVec;
// Get each column from this row
while(getline(ss, tempString, '\t'){
// Put each column into a vector
tempVec.push_back(tempString);
}
// Put the entire vector into our list vector
list.push_back(tempVec);
}
The benefit of this second method is twofold. First, it's very easy. I'm guessing you don't know how it works, but some easy Google searches on keywords you don't know, and you'll find out fast enough. The second is it allows (theoretically) unlimited rows, and unconstrained columns. By that, I mean one row could have 20 columns, one could have 2, and there would be no wasted space.
Note that you should NOT use the skeleton code I showed before researching it. If you don't have at least a general idea of what is happening here, then you'll just cause problems for yourself later on. I'm not going to explain everything here, because other people have done that already. Also, since you're learning this in school, you'll get to these things eventually, so you'll just be getting ahead. The one main constraint would be if your project requires arrays, in which case, my first solution would be the best option.