Extra lines of zeros in output after reading from a file - c++

I am a beginner programer trying to learn C++. I am trying to read the following information from a file:
Dean DeFino 88 98 99
Sally Johnson 78 89 82
Bill Benny 75 79 81
Thomas Billows 78 84 89
However, I get the following output and I can't figure out why:
Dean DeFino 88 98 99
0 0 0
Sally Johnson 78 89 82
0 0 0
Here is the code:
#include <fstream>
#include <iostream>
#include <iomanip>
using namespace std;
const int NAMESIZE = 15;
const int MAXRECORDS = 50;
struct Grades // declares a structure
{
char name[NAMESIZE + 1];
int test1;
int test2;
int final;
};
typedef Grades gradeType[MAXRECORDS];
// This makes gradeType a data type
// that holds MAXRECORDS
// Grades structures.
void readIt(ifstream&, gradeType, const int);
int main()
{
ifstream indata;
indata.open("graderoll.dat");
int numRecord = 4;
gradeType studentRecord;
if(!indata)
{
cout << "Error opening file. \n";
cout << "It may not exist where indicated" << endl;
return 1;
}
readIt(indata, studentRecord, MAXRECORDS);
// output the information
for (int count = 0; count < numRecord; count++)
{
cout << studentRecord[count].name << setw(10)
<< studentRecord[count].test1
<< setw(10) << studentRecord[count].test2;
cout << setw(10) << studentRecord[count].final << endl;
}
return 0;
}
void readIt(ifstream& inData, gradeType gradeRec, const int max)
{
int total = 0;
inData.get(gradeRec[total].name, NAMESIZE);
while (inData)
{
inData >> gradeRec[total].test1;
inData >> gradeRec[total].test2;
inData >> gradeRec[total].final;
total++;
inData.clear();
inData.get(gradeRec[total].name, NAMESIZE);
}
}
Any suggestions to help me out with this?

I SOLVED THE ISSUE:
void readIt(ifstream& inData, gradeType gradeRec, const int max)
{
int total = 0;
inData.get(gradeRec[total].name, NAMESIZE);
while (inData)
{
inData >> gradeRec[total].test1;
inData >> gradeRec[total].test2;
inData >> gradeRec[total].final;
total++;
inData.ignore(NAMESIZE, '\n');
inData.get(gradeRec[total].name, NAMESIZE);
}
}
inData.ignore(NAMESIZE, '\n'); will make the character array work properly. This is because the character array is limited to 15 characters. So if specify to ignore the next 15 characters and or until a newline escape sequence is encountered, each line of the file is read properly.

The problem is your use of:
while (inData)
When converting an iostream to a boolean, the boolean reflects the state of the EOF flag. However, the EOF flag is not set until after you try to read data from the end of the file.
So what happens is your code successfully reads the last line of your file, then loops around and the EOF flag is not set yet, and tries to read one more line from the file. This doesn't read any actual data and you get zeros. After that, the EOF flag is set and your loop terminates.
One way to fix this might be to test the EOF flag after reading the expected data, so:
while (true)
{
inData >> gradeRec[total].test1;
inData >> gradeRec[total].test2;
inData >> gradeRec[total].final;
if (!inData) {
break;
}
total++;
inData.clear();
inData.get(gradeRec[total].name, NAMESIZE);
}
You could also rearrange your loop a bit so you don't have code to read the name twice, once outside the loop and once at the end.

Related

How do I put integer values from an external file into an array to list each value as an output? C++

I am trying to do an assignment in which I generate a file with random integer values and then take those values and put them into an array, however, I am not very sure how to do this because I have only been getting errors with how I am trying to do it so I'm wondering if there is a different way to go about doing this task. I have also attached my code and the external text file with the random integers, you might notice there are some other functions but those are just for other requirements in the assignment and to create the txt file.
#include <fstream>
#include <cstdlib>
using namespace std;
//function 1 Create File with rand numbers
void createfile() {
srand(time(0));
int counter = 0;
int generatednum = 0;
ofstream randNums("randomnumbers.txt");
for (int counter = 0; counter < 50; counter++) {
int generatednum = rand() % 105 + 2;
randNums << generatednum << endl;
}
randNums.close();
}
//function 2 count number of apples picked under 25
void countundertwentyfive() {
int under25count;
int under25amount = 0;
ifstream infile;
infile.open("randomnumbers.txt");
if (infile.fail())
{
cerr << "File failed to open:inputfile.txt";
abort();
}
while (!infile.eof()) {
infile >> under25count;
if (under25count < 25) {
under25amount++;
}
}
cout << under25amount << " people picked under 25 apples." << endl;
infile.close();
}
//function 3 count number of apples picked btwn 80 and 100
void btwn80and100() {
int eightyand100apples;
int under80and100amount = 0;
ifstream infile;
infile.open("randomnumbers.txt");
if (infile.fail())
{
cerr << "File failed to open:inputfile.txt";
abort();
}
while (!infile.eof()) {
infile >> eightyand100apples;
if (eightyand100apples >= 80 && eightyand100apples <= 100) {
under80and100amount++;
}
}
cout << under80and100amount << " people picked between 80 and 100 apples." << endl;
infile.close();
}
//function 4 Find average number of apples picked
void averageapplepicked() {
int numbergetter;
int sum = 0;
ifstream infile;
infile.open("randomnumbers.txt");
if (infile.fail())
{
cerr << "File failed to open:inputfile.txt";
abort();
}
while (!infile.eof()) {
infile >> numbergetter;
sum += numbergetter;
}
sum /= 50;
cout << "Approximately " << sum << " apples were picked on average" << endl;
infile.close();
}
//Use of previous functions along with the array thing I dont understand how to do
int main() {
createfile();
countundertwentyfive();
btwn80and100();
averageapplepicked();
int arrayvalues;
int arraydisplay;
ifstream infile;
infile.open("randomnumbers.txt");
if (infile.fail())
{
cerr << "File failed to open:inputfile.txt";
abort();
}
while (!infile.eof()) {
int arraydisplay[50] = { infile >> arrayvalues };
}
cout << arraydisplay[50] << endl;
infile.close();
return 0;
} ```
The txt file generated by the code
3
74
89
20
35
79
12
12
82
98
25
5
59
99
54
46
53
38
98
42
103
55
73
96
98
75
48
42
70
82
50
62
26
90
17
103
24
56
93
46
10
49
33
76
40
44
6
101
43
43
Thank you so much for any help!
It's fairly easy to read from a file.
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
int main() {
std::fstream newfile("random-numbers.txt", std::ios::in);
if (!newfile.is_open()) {
// Handle file not opening properly
}
std::vector<int> random_nums;
std::string data;
while(getline(newfile, data)){ // Read line by line
random_nums.push_back(std::stoi(data));
}
newfile.close(); // Close the file
}
I use a std::vector here in case you don't know the length of your file. Once you have your vector you can operate on each value or iterate through it however you like.

Reading until the end of a line from a file and repeating the process for the successive line

I'm working with this .txt file:
Anna 70 79 72 78 71 73 68 74 75 70
Jason 78 89 96 91 94 95 92 88 95 92
kim 83 81 93 85 84 79 78 90 88 79
Maria 93 100 86 99 98 97 96 95 94 92
I am storing the name in each line in a vector of struct that contains a string name and an int score. In the score I store te average of all numbers after the name (10 numbers / 10).
I've done all already and it's storing the first line with correct name and average score but what I am struggling with is to repeat this process for the following data. (Jason + his numbers, kim...)
struct Student
{
string name;
int score;
};
void printInfo(vector<Student>);
int main() {
string defaultPath = "lab2.txt";
ifstream inFile(defaultPath);
vector<Student> studentData;
string name;
int score = 0, totalScore = 0, averageScore = 0;
while (inFile >> name)
{
while (inFile >> score)
{
totalScore += score;
}
averageScore = totalScore / 10;
studentData.push_back({name, averageScore});
}
cout << studentData[0].name << '\t' << studentData[0].score << endl;
}
Output of this code:
Anna 73
(The name in the first line + average calculated)
studentData[1].name / .score has garbage on it.
Thank you for your help!
The problem with your current code is that when you read the scores you don't really know when the scores end. The loop will fail only when you read the next lines name, which will put the stream into an error state and no more will be read.
The way I propose that you solve your problem (and make the code more robust) is to
Read line by line into a string
Put the string into an input string stream
Read the name from the input string stream
Read the "scores" in a loop (like you do know) from the input string stream.
Done, onto the next line (go back to 1)
Other possible solutions is to clear any error state after reading the scores. Or use a for loop. Both of these requires you to hope that no line is malformed, or that there are no real errors reading the file.
The problem has been pointed out by #smac89's answer. I want to suggest a slightly different answer.
Read lines of text untill there is nothing to read.
Process each line of text separately using a std::istringstream.
I would also suggest:
Declare variables only in the scope where they are needed.
Don't assume there are 10 numbers. Count the number of numbers successfully read and use the count to compute the average.
std::string line;
while (inFile >> line)
{
std::string name;
std::istringstream str(line);
if ( !(str >> name) )
{
// Deal with error.
}
int count = 0;
int score = 0;
int totalScore = 0;
while (str >> score)
{
totalScore += score;
++count;
}
if ( count > 0 )
{
int averageScore = totalScore / count;
studentData.push_back({name, averageScore});
}
}
here's an easy and useful way for you to use in the future :)
#include <fstream>
#include <sstream>
struct Student
{
string name;
int score;
};
void printInfo(vector<Student>);
int main() {
string defaultPath = "lab2.txt";
ifstream inFile(defaultPath);
vector<Student> studentData;
string line;
string name;
string Sscore;
int totalScore = 0, averageScore = 0;
// first read the line
while (getline(inFile,line))
{
totalScore = 0; averageScore = 0;
stringstream temp(line);
// read the first word which is delimited by a space
getline(temp, name,' ');
// read the rest words which are delimited by a space in the same line
while (getline(temp, Sscore, ' '))
{
totalScore += atoi(Sscore.c_str());
}
averageScore = totalScore / 10;
studentData.push_back({ name, averageScore });
}
cout << studentData[1].name << '\t' << studentData[1].score << endl;
inFile.close();
}
The problem is the inner loop. When it fails to read a number, it will exit leaving the stream in an error state; which causes the outer loop to also fail.
Short of suggesting you use nested stringstreams, here is the easiest way to mitigate this problem:
while (inFile >> name)
{
while (inFile >> score)
{
totalScore += score;
}
averageScore = totalScore / 10;
studentData.push_back({name, averageScore});
if (!inFile.eof()) {
inFile.clear();
}
}
This essentially clears any error states the stream might be in, but only does so as long as the eof bit is not set.
From running your code, I see you do not reset totalScore to zero before doing the next calculation.

What is a good way to read in and separate information from this text file?

Let's say I have a text file:
83 71 69 97Joines, William B.
100 85 88 85Henry, Jackson Q.
And I want to store each number in an array of ints, and each full-name into an array of strings (a full name would be Joines, William B for example).
What would be the best way, because I debated whether using while (inputFile >> line) or while (getline(inputFile, line)) would be better. I don't know if it would be easier to read them one word at a time or read them one line at a time. My main problem will be splitting the 97Joines, William B. to 97 and Joines, William B. which I don't understand how to do in C++.
#include <iostream>
#include <string>
#include <fstream>
using namespace std;
int main() {
int counter = 0;
int scores[40];
string names[10];
string filename, line;
ifstream inputFile;
cout << "Please enter the location of the file:\n";
cin >> filename;
inputFile.open(filename);
while (inputFile >> line) {
// if line is numeric values only, do scores[counter] = line;
// if it is alphabet characters only, do names[counter] = line;
//if it is both, find a way to split it // <----- need help figuring out how to do this!
}
inputFile.close();
}
You need to #include <cstdlib> for strtol I am sure there are better ways to do this but this is the only way I know and this is only for 97joines, and 85Henry,
string word; // to get joines,
string str; // for 97
string numword;
inputFile >> numword;
for(int k = 0; k < numword.length(); k++)
{
if(isdigit(numword[k]))
{
str = str + numword[k];
}
else
{
word = word + numword[k];
}
}
int num = strtol(str.c_str(), NULL, 0);
You can, given the file structure you have shown, read it like this:
int a, b, c, d;
std::string name;
for (int i = 0; i < 2; ++i)
{
// read the numbers
inputFile >> a >> b >> c >> d;
// read the name
std::getline(inputFile, name);
// do stuff with the data... we just print it now
std::cout << a << " " << b << " " << c << " " << d << " " << name << std::endl;
}
Since the numbers are space separated it is easy to just use the stream operator. Furthermore, since the name is the last part we can just use std::getline which will read the rest of the line and store it in the variable name.
You can try it here, using std::cin.

reading from text file and changing values c++

#include <iostream>
#include <fstream>
#include <iomanip> // For formatted input
#include <cctype> // For the "is" character functions
#include <cstring> // For strncpy, strncat and strlen functions
#include <cstdlib> // For exit function
using namespace std;
int main() {
ifstream fin; // Declare and name the input file stream object
ofstream fout; // Declare and name the output file stream object
char in_file[51]; // Filename for the input file
char out_file[56]; // Filename for the output file
char c; // Current character in the file
cout << "Enter the filename of the input file: ";
cin >> setw(51) >> in_file; //setting max length of file name
strncpy(out_file, in_file, 50);
strncat(out_file, ".norm", 50 - strlen(out_file));
fin.open(in_file);
if(fin.fail()) {
cout << "Cannot open " << in_file << " for reading.\n";
exit(1);
}
fout.open(out_file);
if(fout.fail()) {
cout << "Cannot open " << out_file << " for writing.\n";
exit(1);
}
while(fin.get(c))
{
/* commented this out to see if a switch statement would output differently
if (isupper(c))
{
c=tolower(c);
putchar(c);
}
if (c=='/n')
{
fout<< endl << endl;
}
if (c=='/t')
{
for(int i=0; i<9; i++)
fout<<" ";
}
*/
switch (c)
{
case '\t' : // replace 'tab' by '8 chars'
fout << " ";
break;
case '\n' : //replace 1 newline with 2
fout<<"\n"<<"\n";
break;
default: // use default case to proccess all data and
if (isupper (c)) { // test upper/lower-case.
char c2 = tolower (c);
fout << c2;
} else {
fout << c;
}
}
fin >> noskipws >> c; // read the next character
}
fin.close();
fout.close();
cout << in_file << " has been normalized into " << out_file << endl;
return(0);
}
What I'm trying to do is have some input text file, append it with .norm and output it normalized with: 1.All tabs replaced with 8 spaces, 2.All upper case to lower case, 3.Double space the text. I thought my code would accomplish this, but I'm getting really weird outputs.
Here's an example of a text input:
DOE JOHN 56 45 65 72
DOE jane 42 86 58 69
doe tom 89 92 75 86
which then was output to:
dejh 64 57
o ae4 65 9detm8 27 6
I have no idea what's going wrong and would really appreciate any help.
while(fin.get(c))
reads a character at the beginning of every iteration of the while loop. But inside the while loop body, right at the end
fin >> noskipws >> c;
reads another character. This second character will be promptly written over by while(fin.get(c)) and never be inspected.
This is shown by the OP's output: Every second character is transformed and written to the file.
Recommendation to OP: Learn to use your IDE's debugger. This was a trivial error that would have been immediately apparent if OP stepped through a few loop iterations.

How to get cursor to next line after using ">>" operator when reading from file

I am trying to read information from a .txt file that looks as shown below.To read the first 2 lines of integers I use the ">>" operator to read them into an array. My problem is that I want to read the next line(in its entirety) to a string so I can convert it into a stream and parse it, however when I try to simply use getline it doesn't actually read anything into the string which got me thinking that the cursor hasn't actually moved to the next line and I was wondering how to do this or any other method that would serve the save purpose. The structure of the txt file is below:
2
10 10 10 10 10 10 20 20 20 15 15 15 15 15 15 15 20 30 20 15 15 10 10 10
765DEF 01:01:05:59 enter 17
ABC123 01:01:06:01 enter 17
765DEF 01:01:07:00 exit 95
ABC123 01:01:08:03 exit 95
My code is show below:
#include<iostream>
#include<fstream>
#include<string>
#include <sstream>
using namespace std;
int main()
{
int arr[24];
int milemarker;
int numberofCases;
ifstream File;
File.open("input.txt");
File >> numberofCases;
for (int i = 0; i < 24; i++)
{
File >> arr[i];
}
for (int i = 0; i < 24; i++)
{
cout << arr[i] << " ";
}
cout << endl;
string line;
getline(File, line);
cout << line;
system("pause");
}
I think you miss getline() call:
#include<iostream>
#include<fstream>
#include<string>
#include <sstream>
using namespace std;
int main()
{
int arr[24];
int milemarker;
int numberofCases;
ifstream File;
File.open("input.txt");
File >> numberofCases;
for (int i = 0; i < 24; i++)
{
File >> arr[i];
}
for (int i = 0; i < 24; i++)
{
cout << arr[i] << " ";
}
cout << endl;
string line;
getline(File, line);
getline(File, line);
cout << line;
system("pause");
}
Operator >> reads tokens between delimiters. By default, space and new line are delimiters. So after last operator >> call in first loop, you still on the same line, and first getline() call reads only new line character.