Reading 2-dimensional array data from text file - c++

I know this question has been asked before on here, but I couldn't find anything that really helped me understand the problem.
I have a text filled called input.txt, which contents look something along the lines of:
4 4
1.0 2.0 3.0 4.0
5.0 6.0 7.0 8.0
9.0 10.0 11.0 12.0
13.0 14.0 15.0 16.0
Where the first line determines the dimensions of the 2D array.
So using fstream I've found how to pull the strings from the file line by line, but my issue is with pulling the individual data out. I've tried using substr and found that that becomes a real headache when you are dealing with doubles that could possibly have a variable amount of decimal places.
Similarly, since this is for an introductory programming course, vectors are disallowed which is what I've normally used for such a thing (actually the reason I have to redo the assignment).
So using C-style arrays, how should I pull this info from input.txt?
My code for pulling from the first line:
int m, n;
if (inputFile.good()) {
std::string line;
std::getline(inputFile, line);
std::string sizeX = line.substr(0, 1);
std::string sizeY = line.substr(2, 1);
m = stoi(sizeX);
n = stoi(sizeY);
}
But this also would not work if the dimensions of the array were larger than 9.
So how can I improve both my first method, and subsequently pulling the remainder of the data into a C-style array for manipulation in memory.
Additionally, the token based methods I've seen, such as:
int a, b;
while (infile >> a >> b)
{
// process pair (a,b)
}
does not explain how you could do this for a variable number of elements per line.
Thank you in advance

Simply use >> in a loop. It will make life much more complex if you want to read a line at a time, using >> will skip all whitespace including spaces and newlines.
ifstream infile("input.txt");
int size;
infile >> size;
double **arr = ... // allocate array based on size
for (int i = 0; i < size; ++i)
for (int j = 0; j < size; ++j)
infile >> arr[i][j];
I've assumed you know how to allocate the C style array. And I've also done no error checking which in a real program you should include. But hopefully I've illustrated how to read values into an array.

Related

Summing comma separated ints from a text file and then storing to an array in C++

I was tasked to read 3 rows of 5 comma separated values from a text file, sum up each column, and store the result in an array called bins. I am struggling to read the ints from the text file as they are comma separated. I first need to clarify how to read just the ints.
My next thought was to store the ints from the file into an array called "calc", and use the index of each element to sum up the values. I would then store these results into the "bins" array.
Here is some code I have tried to read the comma separated ints yet I cannot seem to get it to work.
int a,b,c,d,e,f,g,h,i,j,k,l,m,n,o;
int calc[15] = {a,b,c,d,e,f,g,h,i,j,k,l,m,n,o};
ifstream myfile;
myfile.open("values.txt");
for(int i = 0; i <= 15; i++)
{
myfile >> calc[i];
myfile.close();
a = calc[0];
b = calc[1];
c = calc[2];
d = calc[3];
e = calc[4];
f = calc[5];
g = calc[6];
h = calc[7];
i = calc[8];
j = calc[9];
k = calc[10];
l = calc[11];
m = calc[12];
n = calc[13];
o = calc[14];
cout << calc[i] << endl;
}
I am really new to working with code and I dont quite understand how to work with values in this manner. It is a simple task yet I cannot seem how to implement it with code.
I am really new to working with code and I dont[sic] quite understand how to work with values in this manner.
OK, I have several tips for you:
① separate your tasks
You ran into a hitch parsing the input in the supplied format, dealing with the comma. Parsing the supplied input files is a totally different problem from the real work, which is summing the columns. Write them separately in the code.
In general you should isolate the "real work" in its own function and have it take parameters as input and returns results as a function return value. The input and output are written separately.
That gives you the added bonus of automating the testing by calling the "work" function with built-in test cases. In this case, it allows you to defer figuring out the parsing. You just pass in test data for now, to get the "work" part working, and then you can come back to parsing the input. Then, when you do need help, it will be specific to "parsing comma separated values" and have nothing to do with why you want them.
② To handle groups of values, you use the array.
This means subscripting or iterating, using loops (or library algorithms) to take what you want to do, written once, and apply it to each value in the array.
Given arrays input and sum, you can accumulate the current row (input) into the running sum with code like this:
for (size_t i = 0; i < COLS; ++i) {
sum[i] += input[i];
}
overall program sketch
open the file
repeat three times:
read a row of input
accumulate the sum with the new input
print the results
Note, as explained in the first topic, that read a row and accumulate the sum are separate functions and separate sub-tasks to figure out. This is called top-down decomposition of a problem.
It's best to use parameters for input and return for output of the function, but for this simple task I'll just use a global variable. Passing/returning is probably harder than the task you are learning! Note though that this is unrealistic in that in real code you would not want to use global variables like this. However, you might turn this into an "object", which you'll learn later.
#include <fstream>
constexpr size_t ROWS = 3;
constexpr size_t COLS = 5;
int input[COLS];
int sum[COLS];
std::ifstream infile;
int main()
{
infile.open("values.txt");
// todo: check for success and feedback to the user if failed
// skipped: zero out the sum array. Global variable start at 0,
// but more generally, you would need to initialize this.
for (size_t row= 0; row < ROWS; ++row) {
read_row();
sum_row();
}
print_results();
}
The sum_row function is what you saw earlier.
Note that with top-down decomposition, you can stub out parts that you will work on later. In particular, you can have read_row return hard-coded result at first, or read from a different format, so you can test the overall program. Then, go back and get that part working for real.
Top-Down Decomposition is critical for any kind of programming project.
Oops... most of your code is useless, and what remains is not really good.
Writing good programs is not a matter of adding C++ instructions one after the other. You must first design the overall structure of your program.
Here you have an input file containing lines of 5 comma separated values and want to compute an array (of size 5) containing the sum of the columns.
Let go from a high level
open the file
loop over the lines
loop 5 times:
read a field up to a comma (or end of the line)
decode that field into an int value
sum that value into an array
close the file
Ok, to be able to sum the values into an array, we will have to define the array before the loop and initialize its elements to 0.
Ok, C++ provide std::ifstream to read a file, std::getline to read a stream up to a delimiter (default being newline), std::istringstream to read the content of a string as an input stream and std::stoi to decode a string representing an int value.
Once this is done, but only after:
the program structure is clearly designed
the required tools from the standard library have been identified
it is possible to sit down in front of your keyboard and start coding.
BTW, this program will never require the declaration of 15 variables a to o nor an array of 15 integers: only int bins[5]...
It could be (tests omitted for brievety):
int bins[5] = {0}; // initializing the first value is enough, others will be 0
std::ifstream in("values.txt");
std::string line;
while (std::getline(in, line)) {
// std::cout << line << '\n'; // uncomment for debug
std::stringstream ss(line);
for(int& val: bins) { // directly loop over the bins array
std::string field;
std::getline(ss, field, ',');
val += std::atoi(field.c_str());
}
}
Of course, for a professional grade (or simply robust) program, every input operation should be followed by a test on the stream...
You can use the std::getline function within the string library to get each comma separated integer.
std::ifstream myfile("values.txt");
for(int i = 0; i < 15; i++)
{
std::string integer_as_string;
std::getline(myfile, integer_as_string, ',');
calc[i] = std::stoi(integer_as_string);
}
myfile.close();
Here we specify that the getline function will read a line of characters in the input until a , character is found. This string is assigned to the integer_as_string variable which will then be converted to an integer and gets assigned to the array.
Also note that i <= 15 will result in undefined behavior. You can further read it here: Wikipedia. And the myfile.close() function was set inside the for loop. This means that in every iteration, you will be closing the file. This is not needed. I think what your looking for is something like this.
std::ifstream myfile("values.txt");
for(int i = 0; i < 15; i++)
{
std::string integer_as_string;
std::getline(myfile, integer_as_string, ',');
calc[i] = std::stoi(integer_as_string);
std::cout << calc[i] << std::endl;
}
myfile.close();
a = calc[0];
b = calc[1];
c = calc[2];
d = calc[3];
e = calc[4];
f = calc[5];
g = calc[6];
h = calc[7];
i = calc[8];
j = calc[9];
k = calc[10];
l = calc[11];
m = calc[12];
n = calc[13];
o = calc[14];
References:
std::stoi
Why is "using namespace std;" considered bad practice?
First, your array have element with indices from 0 to 14, thus for(int i = 0; i <= 15; i++) should be for(int i = 0; i < 15; i++)
The loop itself might benefit from error-checking. What if file contains less than 15 values?
for(int i = 0; i <= 15; i++)
{
// you want check status of myfile here.
}
myfile >> calc[i] wouldn't work well with commas unless you add comma to a separator class for that stream. Albeit that can be done that's a little large change and one can use getline (see answers here for examples) instead to specify separator.
If you want named variables to refer to element of array, you can make them references and structurally bind them to array (or other tuple-like data structure, e.g. struct, etc.) provided you have access to C++17
int calc[15] = {};
auto& [a,b,c,d,e,f,g,h,i,j,k,l,m,n,o] = calc;
a would become a reference to calc[0], b to calc[1] and so on.

Read from text file and store each value in separate arrays in c++

I am trying to read the following contents:
rima doha 44881304 20 30 10 10 20 10102 10102
andrew ny 123456 12 12 13 14 15 01020 03040
and store them in separate arrays, edit them, then store again into the same file.
Here is my attempted code:
ifstream infile;
infile.open("D:\\customers.txt");
string names[100];
string addresses[100];
int tn[100];
int numCalls[100];
int trunkCalls[100];
int numMobCalls[100];
int isdnNum[100];
int dueD[100];
int paymentD[100];
int numOfPpl = 0;
int numOfPpl = 0;
for(int i=0; i<100; i++){
infile >> names[i] >> addresses[i]>>tn[i]>>numCalls[i]>>trunkCalls[i]>> numMobCalls[i]>> isdnNum[i]>>dueD[i]>>paymentD[i];
numOfPpl++;
}
//Code where some values were edited
ofstream outfile("D:\\customers.txt");
for(int i=0; i<numOfPpl; i++)
{
outfile<<names[i] << "\t" <<addresses[i] << "\t" <<tn[i]<<"\t" <<numCalls[i]<<"\t"
<<trunkCalls[i]<<"\t"<<numMobCalls[i]<<"\t"<<numMobCalls[i]<<"\t"<<isdnNum[i]<<"\t"<<dueD[i]<<"\t"<<paymentD[i]<<endl;
}
outfile.close();
infile.close();
Issue is that the first two lines are stored correctly, but then there are random values in the file. How do I fix this?
There's a couple things wrong with your code.
First, you declare numOfPpl twice. Get rid of the 2nd count.
Secondly, you have 9 input categories (names, addresses, etc.), but your text file has 10 per line. This will throw the entire program off.
The third issue is are you going to have exactly 100 lines in the customers.txt? If not, you should utilize a command to peek to the next line to make sure if there's another line below. Using a while or do/while loop would probably be better if there aren't 100 lines in your text file. Something like
while(infile){
// retrieve your data
infile.peek();
}
or utilizing the for loop
for(int i = 0; infile; i++){
// retrieve your data
infile.peek();
}
would probably be a better loop. Again, if you're not going to have 100 lines, the for loop will give you the null output because there isn't any data being put into the array elements. I believe that should correct your issues.
Another thing you should watch out for is having an extra space at the end of a line. That'll throw your program output off as well.

What am I doing wrong with the input function?

So for my second C++ class, we have to write a program that creates an array of 5 structs and a class. In the struct is an int, an array of 5 doubles, and a character array of 81 elements. Of course one of the functions is to take in information from the user. That function is called setStructData() :
void Prog1Class::setStructData()
{
for(int i=0; i<5; i++)
{
cout<<"input an integer, five doubles, and a character array up to 80 characters."<<endl;
cin>>StructArray[i].m_iVal;
for(int j=0; j<5; j++)
{
cin>>StructArray[i].m_dArray[j];
}
cin.ignore('\n');
cin>>StructArray[i].m_sLine;
cout<<"String entered: "<<StructArray[i].m_sLine<<endl;
}
}
The next function getStructData() is to take each element in the array of structs and cout to the user the element on one line, the next element on the next line and so on and so on until all 5 elements on the array are shown. This is what I have:
void Prog1Class::getStructData(int index, Prog1Struct *struct_ptr)
{
struct_ptr=&StructArray[index];
cout<<struct_ptr->m_iVal<<" ";
for(int i=0; i<5; i++)
{
cout<<struct_ptr->m_dArray[i]<<" ";
}
cout<<struct_ptr->m_sLine<<endl;
}
This program works when I enter by keyboard input (which is inputting for each element in the structarray on one line), but my professor has given us a text file test.txt that we're supposed to use and it looks like this (with the character array on a different line than the rest):
10 1.2 2.3 3.4 4.5 5.6
Test string 1
20 2.3 3.4 4.5 5.6 6.7
Test string 2
30 3.4 4.5 5.6 6.7 7.8
Test string 3
40 4.5 5.6 6.7 7.8 8.9
Test string 4
50 5.6 6.7 7.8 8.9 9.1
Test string 5
Its the text file that's screwing my program up because it has the character array on a totally different line. I believe its just the way I'm inputting the data, but I don't know how to fix it. Could anybody help me?
When I use I/O redirection to use the test.txt file to input my data, I get it to output back the first line (without the string) and then the rest is a bunch of crazy junk. Please help me!d
First off, you should always verify that your input was successful! That is, always check after reading that the stream is still in good state, e.g.:
if (!(std::cin >> StructArray[i].m_dArray[j])) {
std::cout << "failed to read double value\n";
}
Next, when using the formatted input into character array, you shall always first set the width! Without setting the width you create an attack vector into your program which can be hacked (it may not matter for your homework assignment but once you create professional software it may very well matter):
if (!(std::cin >> std::setw(sizeof(StructArray[i].m_sLine)) >> StructArray[i].m_sLine)) {
std::cout << "failed to read string\n";
}
Note, however, that the formatted input always stops reading at the first whitespace character. The input from the file seems to contain strings of the form Test string which would result in Test being read and string being left in the stream for the next item to be read. You can use getline() to read all input until the end of the line. However, since all the formatted input operations tend to leave space characters, e.g., the newline after the numbers, in the stream you should probably read leading whitespace. For example, you could use
if (!(std::cin >> std::ws).getline(StructArray[i].m_sLine, sizeof(StructArray[i].m_sLine)) {
...
}
Personally, I would prefer to use std::getline(std::cin >> std::ws, str) with the second argument being of type std::string but it seems your assignment doesn't allow the use of the std::string class. The manipulator std::ws reads all leading whitespace.

Loading Data From Comma Separated Text File to 2D Array?

I am trying to load data from a text file that looks like this:
161,77,88,255
0,44,33,11,111
etc. I have functions to manipulate it, and am ensured that the array is the correct size (which may still vary). Below is my attempt at implementation:
bool loadData(int **imgPix, string fileName) {
ifstream inputFile;
inputFile.open(fileName.c_str());
string tempLineRow; //The resulting line from the text file
string tempElementColumn; //The individual integer element
int numberOfCols = 0;
int numberOfRows = 0;
if (!inputFile.is_open()) {
return false;
}
imgPix = new int* [numberOfRows];
while (getline(inputFile, tempLineRow, '\n')) {
stringstream ss;
ss << tempLineRow; //Stringstream version of the line
while (getline(ss, tempElementColumn, ',' )) {
stringstream ss2;
ss2 << tempElementColumn;
ss2 >> numberOfCols;
//Prob? (**imgPix) = *(*(imgPix + numberOfRows) + numberOfCols);
numberOfCols++;
}
numberOfRows++;
}
inputFile.close();
return true;
}
I've marked the line with the double pointer assignment with a comment, because I believe it be the source of my error, although there could be others. I'm not sure how to use the while loop structure I've implemented to iteratively update the 2D array.
Can anyone offer any assistance? Would be greatly appreciated!
imgPix = new int* [numberOfRows]
Here numberOfRows = 0, so you don't allocate enough memory. You need to know dimensions of the array before you allocate the memory.
And then you should also allocate a memory for each row in the array:
imgPix[currentRow] = new int [TotalCols];
For a 2-dimensional rectangular array, it would be more efficient to create 1-dimensional array of TotalRows*TotalCols elements, and then access it using formula A(row, col) = A[row*TotalCols + col].
Your code has several issues, mostly I guess because you did not fully understand how built-in arrays work in C++. The main issue here is that those cannot easily grow dynamically, there is no "resize" operation for those arrays (but you will need one here). So I suggest kindly that you try to rewrite your code using std::vector<int> and std::vector< std::vector<int> >, make sure you use of .resize when you store a new row or column and come back and ask again when you run into problems with this new implementation.
Previous answers are valid, but if contiguous memory is not a requirement, std::deques may be a better choice in this case compared to std::vectors to avoid lots of memory reallocations.

Using multiple instances of getline in C++

I've been working on a class assignment for C++ and we're required to acquire input from a text file and assign those values to an array....one is a string, the second an int, and the third a double.
We've only been introduced to arrays and I don't know anything yet about pointers or linked lists, or any of the higher end stuff, so I feel like I'm somewhat limited in my options. I've worked all day trying to figure out a way to acquire input from the text file and assign it to the appropriate array. I've tried to use getline to read the input file and set a delimiter to separate each piece of data but I get an error when I try to use it more than once. From what I've read, this has to do with how I'm overloading the function but I'm at a loss at resolving it. Every explanation I've read about it goes beyond my current level of familiarity. Right now, I'm focused on this fragment of code:
for (int i = 0; i < EMP_NUM; i++) // Get input from text file for names.
getline(inFile, nameAr[i], '*');
for (int i = 0; i < EMP_NUM; i++) // Input for hours.
getline(inFile, hoursAr[i], '*');
for (int i=0; i < EMP_NUM; i++) // Input for hourly rate.
getline(inFile, hrateAr[i], '*');
I'm trying to use getline three times and write the data to three separate arrays, then make a series of calculations with them later and output them to another text file. The first instance of getline doesn't produce any compiler errors but the latter two do. I'm not quite sure of another solution to get the data into my arrays, so I'm at a loss. Any help would be great!
If I understand correctly you merely have three values in a file: a string, an int and a double. I assume they are delimited by whitespace.
If that is so then you don't need std::getline(). Rather, use the extraction operator:
std::ifstream file("input.txt");
std::string s;
if( ! (file >> s) ) { // a single word extracted from the file
// failure
}
int n;
// ...
1) Instead of three different iteration, use only one
2) Pass string object in getline instead of pointers
string buf;
for (int i = 0; i < EMP_NUM; i++) // Get input from text file for names.
{
getline(inFile, buf, '*');
nameAr[i] = buf;
getline(inFile, buf, '*'); //assuming delimiter is again *
hoursAr[i] = atoi(buf.c_str() ); //C way to doing it...however in c++ u have to use stringstreams....
getline(inFile, buf);
hrateAr[i] = atof(buf.c_str() );;
}
What do the compiler errors say? Are you sure that the error is caused by getline? Maybe it's not because the getline calls but because of multiple declarations of i.