I'm writing a program that takes a text file with results of an ad campaign and need to find the average rating of the campaign for 4 different demographics. I think I have it all figured out just struggling with getting the data from the file and into char and int variables. Do I need to read it all as strings and then convert or can I read them into those variables?
#include <iostream>
#include <fstream>
#include <iomanip>
#include <string>
using namespace std;
int main(){
//declare vars
ifstream fileIn;
string path;
string name;
char yn;
int age;
double rating;
double rate1 = 0;
double rate1Count = 0;
double avg1 = 0;
double rate2 = 0;
double rate2Count = 0;
double avg2 = 0;
double rate3 = 0;
double rate3Count = 0;
double avg3 = 0;
double rate4 = 0;
double rate4Count = 0;
double avg4 = 0;
double totalAvg = 0;
cout << fixed << showpoint << setprecision(2);
// prompt user
cout << "Please enter a path to the text file with results: ";
// get path
cin >> path;
cout << endl;
// open a file for input
fileIn.open(path);
// error message for bad file
if (!fileIn.is_open()){
cout << "Unable to open file." << endl;
getchar();
getchar();
return 0;
}
// read and echo to screen
cout << ifstream(path);
// restore the file
fileIn.clear();
fileIn.seekg(0);
cout << endl << endl;
// get average for demos
while (!fileIn.eof){
fileIn >> name;
fileIn >> yn;
fileIn >> age;
fileIn >> rating;
if (yn != 121 && age < 18){
rate1 += rating;
rate1Count++;
}
if (yn == 121 && age < 18){
rate2 += rating;
rate2Count++;
}
if (yn != 121 && age >= 18){
rate3 += rating;
rate3Count++;
}
if (yn == 121 && age >= 18){
rate4 += rating;
rate4Count++;
}
}
avg1 = rate1 / rate1Count;
avg2 = rate2 / rate2Count;
avg3 = rate3 / rate3Count;
avg4 = rate4 / rate4Count;
cout << yn << age << rating;
// pause and exit
getchar();
getchar();
return 0;
}
The text file
Bailey Y 16 68
Harrison N 17 71
Grant Y 20 75
Peterson N 21 69
Hsu Y 20 79
Bowles Y 15 75
Anderson N 33 64
Nguyen N 16 68
Sharp N 14 75
Jones Y 29 75
McMillan N 19 8
Gabriel N 20 62
Ditch the cout << ifstream(path); ... fileIn.seekg(0); - that's all unhelpful.
For input, use:
while (fileIn >> name >> yn >> age >> rating)
{
...
That will exit when there's some problem getting the input - whether due to invalid characters for the type (e.g. letters when reading a number), or end-of-file.
Do I need to read it all as strings and then convert or can I read them into those variables?
As above, you don't need to, but you can get better quality input validation and error messages for the user if you get each complete line as a string then attempt to parse out the values:
std::string line;
for (int line_num = 1; getline(fileIn, line); ++line_num)
{
std::istringstream iss(line);
if (iss >> name >> yn >> age >> rating >> std::ws &&
iss.eof())
...use the values...
else
std::cerr << "bad input on line " << line_num
<< " '" << line << "'\n";
// could exit or throw if desired...
}
Related
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 1 year ago.
Improve this question
I am working on an assignment, and the instruction is below:
This fifth assignment will allow you to better explore the concept of File I/O within the C++ programming language.
For this assignment, you are going to read-in (using C++ File I/O) the contents of a text file (.txt) and calculate the letter grade for a given student based upon weighted averages and write this out to a text file (.txt). We will use the weights as outlined in the Syllabus for this course: Assignments: 50%, Participation: 10%, Midterm Exam: 20%, Final Exam: 20%. Your program should read-in the grade category and then each of the respective scores separated/delimited by a comma.
Once you have finished reading the contents of the file you will need to invoke a function, calculateLetterGrade, that has a return type of void and two parameters: one of type double with By-Value semantics and the other of type char with By-Reference semantics. Your program should then write this calculated grade to another user specified text file (.txt) before terminating. You are expected to check to ensure that each respective file opens and that you properly close your file(s). The output file should have a score of 85.8 and letter grade B
Here is my code:
#include <iostream>
#include <fstream>
#include <string>
void calculateLetterGrade(double score, char &grade);
int main(){
std::string fileName("");
char grade;
double totalScore(0);
std::cout << "Welcome to the great Grade Calculator!" << std::endl;
std::cout << "Please enter the file you wish to open: ";
std::cin >> fileName;
// open file to read
std::ifstream file(fileName);
//display the file on the console
if(file.is_open())
{
std::cout << "Reading from file " << fileName << "...\n";
std::cout << "Here is your file " << std::endl;
while(std::getline(file, fileName))
{
std::cout << fileName << std::endl;
}
}
else
{
std::cout << "Unable to open file. " << std::endl;
}
// loop upto end of file
while(!file.eof())
{
// read type
std::string gradeCategory("");
file >> gradeCategory; //stream extraction
// assign the weight in each category
double weight(0);
if(gradeCategory == "Assignments")
{
weight = 0.5; //50%
}
else if(gradeCategory == "Participation")
{
weight = 0.1; //10%
}
else if(gradeCategory == "Midterm" || gradeCategory == "Final")
{
weight = 0.2; //20%
}
double total(0), count(0);
// read count of scores for type
file >> count;
// loop for count times
for(int i = 0; i < count; i++)
{
// read score and add to total
double score;
file >> score;
total = total+score;
}
// calculate average and add weighted score to totalScore
double average = total/count;
totalScore = totalScore+(average*weight);
}
// close file
file.close();
// call function to get grade
std::cout << "Calculating grades...\n";
calculateLetterGrade(totalScore,grade);
// read output filename and open it
std::cout << "Save To (Filename): ";
std::cin >> fileName;
std::ofstream fileForGrade(fileName);
// write score to file
fileForGrade << totalScore;
// write grade to file and then close file
fileForGrade << grade;
fileForGrade.close();
std::cout << "Score & Letter Grade written to file: " << fileName << std::endl;
std::cout << "Thank you for using my program, have a bless day!" << std::endl;
return 0;
}
// function to calculate grade by give score
void calculateLetterGrade(double score, char &grade){
// assign 'A' if score greater than or equal to 90
if(score>=90){
grade = 'A';
}
// assign 'B' if score greater than or equal to 80
else if(score>=80){
grade = 'B';
}
// assign 'C' if score greater than or equal to 70
else if(score>=70){
grade = 'C';
}
// assign 'D' if score greater than or equal to 60
else if(score>=60){
grade = 'D';
}
// assign 'F', means fail
else{
grade = 'F';
}
}
My code compiles, but the output file comes out to be 0 F. Can someone point out what I did wrong in the code?
UPDATE: here is my latest code:
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
void calculateLetterGrade(double score, char &grade);
int main(){
std::string fileName("");
char grade;
double totalScore(0);
std::cout << "Welcome to the great Grade Calculator!" << std::endl;
std::cout << "Please enter the file you wish to open: ";
std::cin >> fileName;
// open file to read
std::ifstream file(fileName);
//display the file on the console
if(file.is_open())
{
std::cout << "Reading from file " << fileName << "...\n";
std::cout << "Here is your file " << std::endl;
std::string line;
while(std::getline(file, line))
{
std::cout << line << std::endl;
std::istringstream iss(line);
std::string gradeCategory;
iss >> gradeCategory;
// assign the weight in each category
double weight(0);
if(gradeCategory == "Assignments")
{
weight = 0.5; //50%
}
else if(gradeCategory == "Participation")
{
weight = 0.1; //10%
}
else if(gradeCategory == "Midterm" || gradeCategory == "Final")
{
weight = 0.2; //20%
}
double total(0);
int count(0);
// read count of scores for type
iss >> count;
// loop for count times
for(int i = 0; i < count; ++i)
{
// read score and add to total
double score;
iss >> score;
total += score;
}
// calculate average and add weighted score to totalScore
double average = total/count;
totalScore = totalScore+(average*weight);
}
}
else
{
std::cout << "Unable to open file. " << std::endl;
}
// close file
file.close();
// call function to get grade
std::cout << "Calculating grades...\n";
calculateLetterGrade(totalScore,grade);
// read output filename and open it
std::cout << "Save To (Filename): ";
std::cin >> fileName;
std::ofstream fileForGrade(fileName);
// write score to file
fileForGrade << totalScore;
// write grade to file and then close file
fileForGrade << " " << grade;
fileForGrade.close();
std::cout << "Score & Letter Grade written to file: " << fileName << std::endl;
std::cout << "Thank you for using my program, have a bless day!" << std::endl;
return 0;
}
// function to calculate grade by give score
void calculateLetterGrade(double score, char &grade){
// assign 'A' if score greater than or equal to 90
if(score>=90){
grade = 'A';
}
// assign 'B' if score greater than or equal to 80
else if(score>=80){
grade = 'B';
}
// assign 'C' if score greater than or equal to 70
else if(score>=70){
grade = 'C';
}
// assign 'D' if score greater than or equal to 60
else if(score>=60){
grade = 'D';
}
// assign 'F', means fail
else{
grade = 'F';
}
}
And here is my input file named grade.txt:
Assignments
75,86,90,80,95,100
Participation
90
Midterm
75
Final
90
After the 1st while loop reads all of the file, file's position is at the end of the file. You need to call file.seekg(0) to reset the position back to the beginning of the file before you can read the contents again in the 2nd while loop.
std::ifstream file(fileName);
...
while(std::getline(file, fileName))
{
std::cout << fileName << std::endl;
}
file.seekg(0); // <-- ADD THIS!
...
That being said, consider combining the 2 loops. You can use std::istringstream to parse each line that is read.
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
...
std::ifstream file(fileName);
if (file.is_open())
{
...
std::string line;
while (std::getline(file, line))
{
std::cout << line << std::endl;
std::istringstream iss(line);
std::string gradeCategory;
iss >> gradeCategory;
...
int count = 0;
iss >> count;
double total = 0.0;
for (int i = 0; i < count; ++i)
{
double score;
iss >> score;
total += score;
}
...
}
}
else
{
std::cout << "Unable to open file. " << std::endl;
}
...
UPDATE: given that we can now see what you input file actually looks like, the code shown so far will not be able to read it correctly. You will have to make some additional logic changes to the reading loop, eg:
...
std::string line;
while (std::getline(file, line))
{
std::cout << line << std::endl;
// assign the weight in each category
std::string gradeCategory = line;
double weight = 0.0;
if (gradeCategory == "Assignments")
{
weight = 0.5; //50%
}
else if (gradeCategory == "Participation")
{
weight = 0.1; //10%
}
else if (gradeCategory == "Midterm" || gradeCategory == "Final")
{
weight = 0.2; //20%
}
// read scores and add to total
std::getline(file, line);
std::istringstream iss(line);
double score, total = 0.0;
int count = 0;
char comma;
while (iss >> score)
{
total += score;
++count;
iss >> comma;
}
if (count != 0)
{
// calculate average and add weighted score to totalScore
double average = total / count;
totalScore += (average * weight);
}
}
...
Demo
I am trying to read student information from one file onto another but i keep getting a Segmentation Fault error. I am not sure were it is that I am going wrong. I am trying to read in the first and last name as well as the test score in order to calculate the letter grade, and output all of that in another file. I will then try to determine which student has the greatest score and display the score as well as their name.
#include <iostream>
#include <fstream>
#include <string>
#include <iomanip>
using namespace std;
// Set global Variables.
ifstream fIn;
const int MAX = 35; // number of students
struct STUDENTS{
string fname;
string lname;
int testScore;
char grade;
};
void input(ifstream &);
void calculateGrade(STUDENTS);
int main(){
input(fIn);
}
void input(ifstream &fIn){
ofstream fOut;
fIn.open("students.txt");
fOut.open("students.out");
if (!fIn){
cout << "Cannot open input file. Program terminates!." << endl;
return;
}
int i;
for(i = 0; i < MAX; i++){
STUDENTS student[i];
fIn >> student[i].fname >> student[i].lname >> student[i].testScore;
// switch statement that calculates grade goes here
calculateGrade(student[i]);
fOut << left << setw(12) << student[i].lname << ", " << setw(12) <<
student[i].fname << right << setw(4) << student[i].testScore <<
setw(2) << student[i].grade << endl;
}
fOut << endl;
fIn.close();
fOut.close();
}
void calculateGrade(STUDENTS student){
if(student.testScore >= 90 && student.testScore <= 100){
student.grade = 'A';
}else if(student.testScore >= 80 && student.testScore <= 89){
student.grade = 'B';
}else if(student.testScore >= 70 && student.testScore <= 79){
student.grade = 'C';
}else if(student.testScore >= 60 && student.testScore <= 69){
student.grade = 'D';
}else if(student.testScore >= 0 && student.testScore <= 59){
student.grade = 'F';
}else{
student.grade = 'N';
}
}
for my csc 102 assignment, I need to create a class to hold a student's grades and name. Then output that information to a text file. The grades and name are both read from an input file. I got it to succesfully run one instance of student. However, I do not know how to make the next student object read from the next line.
the input file is in this format:
Jonathan Blythe 87 76 79 88
Jessica Blake 87 79 58 86
Jonathan Lee 88 86 69 100
Joseph Blake 78 89 50 69
My first Student object Student a; reads the correct line. However, when I call the function again for another Student object Student b;, it still reads the first line, and overwrites the output file. I thought if I didn't close the file until the end of main that it may read correctly. I will show the class header file, and the implementation file for Student below.
#include "Student.h"
Student::Student() {
cout << "Default Constructor" << endl;
}
void Student::getscores() {
ifstream infile;
infile.open("input.txt");
infile >> firstName >> lastName;
for (int i = 0; i <= 3; i++) {
infile >> scores[i];
}
infile.close();
}
void Student::getaverage() {
average = 0;
for (int i = 0; i < 4; i++) {
average = average + scores[i];
}
average = average / 4;
}
void Student::print()const {
ofstream outfile;
outfile.open("output.txt");
outfile << firstName << " " << lastName << endl;
cout << firstName << " " << lastName << endl;
for (int i = 0; i <= 3; i++) {
cout << scores[i] << " ";
outfile << scores[i] << " ";
}
cout << endl;
outfile << endl;
cout << "Average Score: " << average << endl;
outfile << "Average Score" << average << endl;
cout << "Letter Grade: " << grade << endl;
outfile << "Letter Grade: " << grade << endl;
//outfile.close();
}
void Student::getletter() {
if (average >= 90)
grade = 'A';
else if (average >= 80 && average < 90)
grade = 'B';
else if (average >= 70 && average < 80)
grade = 'C';
else if (average >= 60 && average < 70)
grade = 'D';
else if (average < 60)
grade = 'F';
}
Student::~Student() {
}
and
#pragma once
#include<iostream>
#include<string>
#include <fstream>
using namespace std;
class Student
{
string lastName;
string firstName;
int scores[4] = { 0,0,0,0 };
int average = 0;
char grade = 'n';
public:
Student();
Student(string, string, int, int, int, int, char);
~Student();
void getscores();
void getaverage();
void getletter();
void print()const;
};
How do I read incrementally to the next line everytime a function is called?
One option is to pass the input stream as an argument to the function.
You should read the input.txt line by line, for each line you need to parse to get the firstName, lastName, scores then use them to create a new object of Student class (you need some changes of Student class to create object from name, set scores, etc.)
I suggest the code skeleton is something like below:
char line[128] = {0,};
ifstream infile;
infile.open("input.txt");
if (!infile.is_open()) {
return;
}
while (infile.getline(line, sizeof(line) - 1)) { // read content of next line then store into line variable
// parse content of line to get firstName, lastName, scores
...
// create new object of Student class from firstName, lastName, scores you got
...
// clear content in line
memset(line, '\0', sizeof(line));
}
So for my assignment I need to read a text file with student names and their test scores and display on screen the average test score and the highest test score.
The content of the text file is:
John Smith 99
Sarah Johnson 85
Jim Robinson 70
Mary Anderson 100
Michael Jackson 92
The code I have so far is:
#include <iostream>
#include <string>
#include <fstream>
using namespace std;
void inputFile(string, string, int, int, int, int);
int main()
{
string firstName;
string lastName;
int testScore = 0;
int totalScore = 0;
int avgScore = 0;
int highestScore = 0;
inputFile(firstName, lastName, testScore, totalScore, avgScore, highestScore);
system("pause");
return 0;
}
void inputFile(string firstName, string lastName, int testScore, int totalScore, int avgScore, int highestScore)
{
ifstream myFile("scores.txt");
int i = 0;
while (myFile >> firstName >> lastName >> testScore) {
totalScore = totalScore + testScore;
i++;
}
avgScore = totalScore / i;
cout << "Average score: " << avgScore << endl;
while (myFile >> firstName >> lastName >> testScore) {
if (highestScore < testScore) {
highestScore = testScore;
}
}
cout << "Highest score: " << highestScore << endl;
}
When I run the program it displays the average score correctly but when it comes to the highest score it just displays "0" every time rather than displaying "100" which is the largest number in the text file. How would I make it display "100" for 'highestScore' rather than "0"?
while (myFile >> firstName >> lastName >> testScore) {
if (highestScore < testScore) {
highestScore = testScore;
}
}
Why are you attempting to read the file again? You should have processed it at the same time as summing up:
while (myFile >> firstName >> lastName >> testScore) {
totalScore = totalScore + testScore;
if (highestScore < testScore) {
highestScore = testScore;
}
i++;
}
Or alternatively, rewind the file before trying to read again:
myfile.clear();
myfile.seekg(0);
while (myFile >> firstName >> lastName >> testScore) {
/* stuff... */
With the first loop you go through the file all the way to the end. And then it stays on the end, it will not "rewind" to the beginning automatically.
Either you have to seek back to the beginning for the second loop (and clear the end-of-file status). Or calculate the highest score in the first loop too.
I'm a very novice programmer, and I'm trying to make a program that reads a txt file containing the names of 5 students (first names only) as well as four exam scores for each student. I'm trying to read the names into an array called students, then read the scores into 4 separate arrays named test1, test2, test3, test4, then display it from the monitor. The file looks like this:
Steve 78 65 82 73
Shawn 87 90 79 82
Annie 92 90 89 96
Carol 72 65 65 60
Kathy 34 50 45 20
I'm having a very hard time with breaking up the arrays and organizing them. Can someone help me? Please keep in mind I'm very novice, so I don't know a large amount about programming.
This is my code thus far:
#include <cstdlib>
#include <iostream>
#include <string>
#include <fstream>
#include <iomanip>
#include <ctime>
#define over2 "\t\t"
#define over3 "\t\t\t"
#define over4 "\t\t\t\t"
#define down5 "\n\n\n\n\n"
using namespace std;
int main(int argc, char *argv[])
{
ifstream inputFile;
//Constant for max string size
const int SIZE = 5;
//Constant for the scores
string names[SIZE], fileName, line, test1[SIZE], test2[SIZE], test3[SIZE], test4[SIZE];
//input section, user enters their file name
cout << down5 << down5 << over2 << "Please enter your file name: ";
cin >> fileName;
system("CLS");
//open the file containing the responses
inputFile.open(fileName.c_str());
cout << endl;
//kicks you out if file isn't found
if (inputFile)
{
for(int i = 0; i < SIZE; i++)
{
getline(inputFile, line);
names[i] = line;
getline(inputFile, line);
test1[i] = line;
getline(inputFile, line);
test2[i] = line;
getline(inputFile, line);
test3[i] = line;
getline(inputFile, line);
test4[i] = line;
}
inputFile.close();
}
cout << down5 << over3 << "Student\tTest1\tTest2\tTest3\tTest4\n";
cout << over3 << "-------\t-----\t-----\t-----\t-----\n";
for(int i = 0; i < SIZE; i++)
{
cout << over3 << names[i] << endl;
cout << over3 << test1[i] << endl;
cout << over3 << test2[i] << endl;
cout << over3 << test3[i] << endl;
cout << over3 << test4[i] << endl;
}
return 0;
}
Let's look at the structure of the file you're trying to read:
Steve 78 65 82 73
Shawn 87 90 79 82
Annie 92 90 89 96
Carol 72 65 65 60
Kathy 34 50 45 20
The format of the data can be described as follows:
Each line represents a single "record".
Each "record" contains multiple columns.
Columns are separated by whitespace.
You're currently using getline() for every column:
for(int i = 0; i < SIZE; i++)
{
getline(inputFile, line);
names[i] = line;
getline(inputFile, line);
test1[i] = line;
getline(inputFile, line);
test2[i] = line;
getline(inputFile, line);
test3[i] = line;
getline(inputFile, line);
test4[i] = line;
}
...whereas you actually want to read in a single line for each record and split it up:
for (int i = 0; i < SIZE; i++)
{
string line;
size_t start = 0;
// For each line, attempt to read 5 columns:
getline(inputFile, line);
names[i] = get_column(line, start);
test1[i] = get_column(line, start);
test2[i] = get_column(line, start);
test3[i] = get_column(line, start);
test4[i] = get_column(line, start);
}
Here's a modified version of your original code, which splits up each line as described above:
#include <cctype>
#include <fstream>
#include <iostream>
#include <string>
using namespace std;
static string get_column(string line, size_t &pos)
{
size_t len = 0;
// Skip any leading whitespace characters.
while (isspace(line.c_str()[pos])) { ++pos; }
// Count the number of non-whitespace characters that follow.
while (!isspace(line.c_str()[pos+len]) && line.c_str()[pos+len]) { ++len; }
// Extract those characters as a new string.
string result = line.substr(pos, len);
// Update the "start" position (for the next time this function is called).
pos += len;
// Return the string.
return result;
}
int main()
{
const int SIZE = 5;
string names[SIZE], test1[SIZE], test2[SIZE], test3[SIZE], test4[SIZE];
// Ask the user to enter a file name.
cout << "Please enter your file name: ";
string fileName;
cin >> fileName;
// Open the file and read the data.
ifstream inputFile(fileName.c_str());
if (!inputFile.is_open()) { return 0; }
for (int i = 0; i < SIZE; i++)
{
string line;
size_t start = 0;
// For each line, attempt to read 5 columns:
getline(inputFile, line);
names[i] = get_column(line, start);
test1[i] = get_column(line, start);
test2[i] = get_column(line, start);
test3[i] = get_column(line, start);
test4[i] = get_column(line, start);
}
inputFile.close();
// Display the data.
cout << "Student\tTest1\tTest2\tTest3\tTest4" << endl;
cout << "-------\t-----\t-----\t-----\t-----" << endl;
for(int i = 0; i < SIZE; i++)
{
cout << names[i] << "\t";
cout << test1[i] << "\t";
cout << test2[i] << "\t";
cout << test3[i] << "\t";
cout << test4[i] << endl;
}
}
Running the program produces the following output:
Please enter your file name: textfile.txt
Student Test1 Test2 Test3 Test4
------- ----- ----- ----- -----
Steve 78 65 82 73
Shawn 87 90 79 82
Annie 92 90 89 96
Carol 72 65 65 60
Kathy 34 50 45 20
Note that the get_column() function handles multiple spaces or too-short lines, so that this file:
Steve 78 65 82 73
Shawn 87 90
Annie 92 90 89 96
Carol 72
Kathy 34 50 45 20
...produces the following output:
Please enter your file name: textfile.txt
Student Test1 Test2 Test3 Test4
------- ----- ----- ----- -----
Steve 78 65 82 73
Shawn 87 90
Annie 92 90 89 96
Carol 72
Kathy 34 50 45 20
The previous answer is overthinking the problem.
You can use your input file stream to retrieve 'formatted input' (that is, input you know to be in a format such as 'string' then 'int', 'int', 'int', 'int') with the >> operator.
string name;
int score[4];
ifstream mf;
mf.open("score.txt");
// Get name string.
mf >> name;
// Get four score values into an array.
mf >> score[0] >> score[1] >> score[2] >> score[3];
And then:
cout << name;
cout << score[0];
cout << score[1];
cout << score[2];
cout << score[3];