c++ converting txt file data into double variables - c++

Hi so I started to create a program to calculate a person's GPA and save that info for future reference. My problem is that I can't figure out how to read the numbers saved in the .txt file and then store them for GPA calculating purposes. Here is the unfinished program's code and any help would be great
EDIT: The .txt file is laid out like this: 5.0 3.7 5.0 3.7 5.0 4.0 ... Below is my progress in the program but when I run it I receive a GPA of 0 (incorrect). Not sure if the lexical cast is my problem, the getline() method or something else. Any help (the calculateGPA() method is the trouble area)?
#include <iostream>
#include <fstream>
#include <string>
#include <boost/lexical_cast.hpp>
using namespace std;
string newCC, newCG;
double curGPA;
char command;
bool running = 1;
void calculateGPA();
void writeGrades();
void mainMenu();
void addClass();
int main()
{
while(running) {
mainMenu();
}
return 0;
}
void calculateGPA()
{
double credit_sum = 0.0, grade_sum = 0.0;
double credit, grade;
ifstream gReader("grades.txt");
for( int i = 0; ! gReader.eof() ; i++ )
{
string number;
getline( gReader , number ) ;
double dblNumber;
try
{
dblNumber = boost::lexical_cast<double>(number);
}
catch (boost::bad_lexical_cast const&)
{
dblNumber = 0;
}
credit_sum = credit_sum + dblNumber;
}
ifstream cReader("credits.txt");
for( int i = 0; ! cReader.eof() ; i++ )
{
string number;
getline( cReader , number ) ;
double dblNumber;
try
{
dblNumber = boost::lexical_cast<double>(number);
}
catch (boost::bad_lexical_cast const&)
{
dblNumber = 0;
}
credit_sum = credit_sum + dblNumber;
}
if(credit_sum == 0.0) {
curGPA = 0.0;
}
curGPA = (grade_sum / credit_sum);
cReader.close() ;
gReader.close() ;
}//End calculateGPA
void writeGrades()
{
string cToWrite = newCC + "\n";
string gToWrite = newCG + "\n";
ofstream cWriter("credits.txt", ios::app);
cWriter << cToWrite;
ofstream gWriter("grades.txt", ios::app);
gWriter << gToWrite;
cWriter.close();
gWriter.close();
}//End writeGrades
void addClass()
{
cout << "New class' credits?"; cin >> newCC;
cout << endl << "New class' grade? (GP)"; cin >> newCG;
writeGrades();
cout << "Add another class? (y/n)" << endl; cin >> command;
if(command == 'y')
addClass();
else mainMenu();
}//End addClass
void mainMenu()
{
string command;
cout << "What would you like to do?" << endl;
cout << "(V)iew GPA" << endl;
cout << "(A)dd grades" << endl;
cout << "(E)xit" << endl;
cin >> command;
if(command == "v")
{
calculateGPA();
cout << "Your current GPA is " << curGPA << endl;
}
else if(command == "a")
{
addClass();
}
else running = 0;
}//End mainMenu

Using streams:
double calculateGPA() {
double credit_sum = 0.0, grade_sum = 0.0;
double credit, grade;
ifstream reader("grades.txt");
while (!reader.eof()) { /* while the file still has numbers to be read */
reader >> credit >> grade;
if (reader.fail()) {
/* something unexpected happened (read error/formatting error) */
break;
}
credit_sum += credit;
grade_sum += grade;
}
if(credit_sum == 0.0) {
/* avoid divide by zero */
return 0.0;
}
/* divide the total grade by the total credits */
return grade_sum / credit_sum;
}
Notes:
Assumes .txt file just has numbers (credits, grade, credits, grade, ...) delimited by whitespace (spaces or
line breaks): For more complicated formatting you want scanf or
regex.
Returns double instead of float, you usually want double's precision
over float's relatively small speed and memory advantage.
File will be closed at the end of the function, when reader goes out
of scope. reader.close() is not necessary, but wouldn't be a bad
thing to put in (I'm just lazy.)

Look at this function: http://www.cplusplus.com/reference/cstdlib/atof/
You could use it to convert the string (or char) being read in into a double. I assume you want double because grades are 4.0, 3.7, 3.3, 3.0, etc.

Or, you can read into a double directly.
calculateGPA()
{
double credit;
...
reader >> credit;
...
}
Also, in your WriteGrades function, why are you writing out new lines (use endl instead of '\n') before your line instead of at the end?

Related

My question = until user press "enter" take average from user inputs in c++

This code calculates the average from user that user inputs integer until user inputs "666" integer. However, I want to make it stop when the user just presses the enter key. How can I achieve this?
#include <iostream>
using namespace std; //s
int main()
{
int total = 0, counter = 0, number, average;
do
{
cin >> number;
if(number == 666) //what should i do to make enter button instead of 666?
{
average = total / counter;
cout << average;
return 0;
}
total = total + number;
counter = counter + 1;
} while(1);
return 0;
}
Sadly, you cannot check that easily if the <ENTER> button has been pressed. cin reads formatted input (numbers in your case) and ignores everything else (including whitespaces, like newline). A solution to your problem is to read a whole line and extract the numbers from it:
#include <iostream> // cin, cout
#include <sstream> // istringstream
#include <string> // getline
int main()
{
// Reading a line from the standard input to the variable 'line'.
std::string line;
std::getline(std::cin, line);
// The easiest way to get the numbers from 'line' is to wrap it in an
// 'istringstream'. Now, you can use 'iss' just like 'cin'.
std::istringstream iss{line};
double total = 0.0;
double counter = 0.0;
for (double number; iss >> number; ++counter) {
total += number;
}
std::cout << "Avarage: " << total / counter << '\n';
return 0;
}
I have been found the solution:
note that I use Code::Blocks compiler and I had to make a adjustment.
Settings> compiler > tick on the """have g++ follow the c++11 ISO C++ language standard [-std=c++11]""" box and click OK.
solution is below:
#include <iostream>
#include <string>
using namespace std;
int main() {
float c = 0, sum = 0;
string input;
while (true) {
cout << "input number:";
getline(cin,input);
if (input == "") {
cout << "average:" << sum / c << endl;
break;
}
sum += stof(input);
c++;
}
return 0;
}

Am I passing parameters correctly?

I'm trying to get the function getFilename to prompt the user for which file to read and then pass that to the function readFile which calculates the numbers in the file and then (using displayAverage) display the average of the numbers in the file.
I'm new to coding and can't figure out why it doesn't seem to be using the readFile function... the program prompts the user for the file but after that it just inputs a blank line. Am I calling the functions & passing the parameters correctly?
void getFilename(char fileName[])
{
cout << "Please enter the filename: ";
cin >> fileName;
return;
}
float readFile(char fileName[])
{
cout.setf(ios::fixed);
cout.precision(0);
ifstream fin(fileName);
int sum = 0;
int numValue = 0;
float grades = 0;
float average= 0;
if (fin.fail())
{
cout << "Error opening file \"" << fileName << "\"";
return false;
}
while (!fin.eof())
{
fin >> grades;
sum += grades;
numValue++;
if (numValue != 10)
cout << "Error reading file \"" << fileName << "\"";
}
fin.close();
average = sum / 10;
return average;
}
void displayAverage(int average)
{
cout << average;
return;
}
int main()
{
char* fileName;
int average;
getFilename(fileName);
readFile(fileName);
displayAverage(average);
return 0;
}
Your program has undefined behaviour since fileName does not point to anything valid that can hold data.
Unless you are required to use an array of chars to hold the filename, use std::string for fileName.
std::string fileName;
If you are required to use an array of chars to hold the filename, use
char fileName[FILENAME_LENGTH];
Make sure FILENAME_LENGTH is large enough for your needs.

Why does this while loop output the same thing no matter what you input?

I have a program that does three things. Asks you how many variables you wan't, ask you to input each variable, then stores it in a vector. I have put some code that checks if your input is correct, and if it isn't, re-loops the code asking for your variable. The problem I am having is that when you type anything in around the second variable, it asks you to try again infinitely.
For instance, if I typed these values into the input:
Variable amount: 5
Please input variable 1: 8
Please input variable 2: 8
ERROR, PLEASE ENTER ONLY VALID SYMBOLS
---------------------
Please input variable 2:
It would keep outputting ERROR, PLEASE ENTER ONLY VALID SYMBOLS over and over again no matter what you typed. The code is down below, and if you have a better name for this question please let me know. (I'm not really sure what to call this)
#include <iostream>
#include <cmath>
#include <string>
#include <algorithm>
#include <vector>
#include <sstream>
using namespace std;
int inputErrorMessage()
{
cout << "\n ERROR, PLEASE ENTER ONLY VALID SYMBOLS \n";
cout << "--------------------- \n";
return 0;
}
int main()
{
// Declare the variables, vectors, etc.
int varNum = 1;
int totVar = 0;
int choice = 0;
vector<int> userNums;
double input = 0;
string checktotVar = "";
string checkInput = "";
string sym = "";
bool valid = false;
stringstream sstotVar;
stringstream ssinput;
if (choice != 6) {
while (!valid) {
valid = true;
// Ask user for how many variables they want then record it
cout << "Variable amount: ";
getline(cin, checktotVar);
sstotVar << checktotVar;
sstotVar >> totVar;
if (sstotVar.fail() || totVar <= 0) {
inputErrorMessage();
valid = false;
sstotVar.clear();
sstotVar.ignore();
}
}
valid = false;
while (!valid) {
valid = true;
// Ask the user for each variable, then record it into the array
for (int i = 0; i < totVar; ++i) {
cout << "Please input variable " << varNum << ": ";
getline(cin, checkInput);
ssinput << checkInput;
ssinput >> input;
if (ssinput.fail()) {
inputErrorMessage();
valid = false;
ssinput.clear();
ssinput.ignore();
}
if (valid == true) {
userNums.push_back(input);
varNum++;
}
}
}
}
}
ssinput >> input;
reads the one thing in ssinput right to the end of the stream while leaving the read valid. The next time around
ssinput << checkInput;
can't write into the stream because the stream hit the stream's end. That means the read also fails and
if (ssinput.fail()) {
enters the body of the if where the program clears the error
ssinput.clear();
and then promptly reads off the end of the stream with
ssinput.ignore();
causing the error all over again.
Quickest solution:
Recreate
stringstream ssinput;
on each loop iteration. So
stringstream sstotVar;
//stringstream ssinput; gone from here
and
getline(cin, checkInput);
stringstream ssinput(checkInput); // and now tighter scope recreated each loop.
ssinput >> input;
Also by keeping the stream around without emptying it out it can get very., very big.
You can also simplify your logic around
while (!valid) {
and eliminate some repeated code by moving the read validation into it's own function
int getMeANumber(const std::string & message, int min)
that loops until it gets a number and then returns that number. For example:
int getMeANumber(const std::string & message, int min)
{
while (true)
{
cout << message;
string checktotVar;
getline(cin, checktotVar);
stringstream sstotVar(checktotVar);
int totVar;
sstotVar >> totVar;
if (!sstotVar || totVar <= min)
{
inputErrorMessage();
}
else
{
return totVar;
}
}
}
Now main is this itty-bitty tiny lil' thing.
int main()
{
int choice = 0;
vector<int> userNums;
if (choice != 6)
{
int totVar = getMeANumber("Variable amount: ", 0);
for (int i = 0; i < totVar; ++i)
{
stringstream varname;
varname << "Please input variable " << i+1 << ": ";
userNums.push_back(getMeANumber(varname.str(), numeric_limits<int>::min()));
// numeric_limits<int>::min requires #include <limits>
}
}
}
Here are the issues with this code.
In this part:
if (valid == true) {
userNums.push_back(input);
varNum++;
}
you forgot to add an ssinput.clear(). This will reset the stream state (clear the error flags), otherwise you cannot use it again. That is why it stops working at the second input.
In addition, even though this works, you are pushing back a variable that you declared as double into a vector of ints. That is bound to cause issues if this was intended to store double variables, instead of truncating them and storing them as ints.
It should be:
#include <iostream>
#include <cmath>
#include <string>
#include <algorithm>
#include <vector>
#include <sstream>
using namespace std;
int inputErrorMessage()
{
cout << "\n ERROR, PLEASE ENTER ONLY VALID SYMBOLS \n";
cout << "--------------------- \n";
return 0;
}
int main()
{
// Declare the variables, vectors, etc.
int varNum = 1;
int totVar = 0;
int choice = 0;
vector<int> userNums;
double input = 0;
string checktotVar = "";
string checkInput = "";
string sym = "";
bool valid = false;
stringstream sstotVar;
stringstream ssinput;
if (choice != 6) {
while (!valid) {
valid = true;
// Ask user for how many variables they want then record it
cout << "Variable amount: ";
getline(cin, checktotVar);
sstotVar << checktotVar;
sstotVar >> totVar;
if (sstotVar.fail() || totVar <= 0) {
inputErrorMessage();
valid = false;
sstotVar.clear();
sstotVar.ignore();
}
}
valid = false;
while (!valid) {
valid = true;
// Ask the user for each variable, then record it into the array
for (int i = 0; i < totVar; ++i) {
cout << "Please input variable " << varNum << ": ";
getline(cin, checkInput);
ssinput << checkInput;
ssinput >> input;
if (ssinput.fail()) {
inputErrorMessage();
valid = false;
}
if (valid == true) {
userNums.push_back(input);
varNum++;
}
ssinput.clear();
}
}
}
}
EDIT: You need to clear the stringstream on each iteration of the loop, otherwise you're not writing to an empty stream when you grab the next input from the user, which is what's causing the .fail() method to return true after the first iteration of the loop.

incorrect stof() declaration

I'm reading in values from a text file and printing the to screen as strings. The idea is to read in each individual string and print them to screen along with the running average of strings read in printed next to it.
i have this for my string to float declaration
int main()
{
string inputfile, intstring;
float counter;
counter = 0;
float average;
average = 0;
float stringconv = stof(intstring);
cout << "Enter the name of a file to open\n";
cin >> inputfile;
ifstream inFile;
inFile.open(inputfile.c_str());
and later on to calculate the average
while (!inFile.eof())
{
getline(inFile, intstring, ' ');
cout << intstring <<","<<average<< endl;
//increments counter to keep average output correct
counter = counter +1;
//goes to next line at each space encountered in text file
average = (counter + stringconv) /2;
}
I've included this just in case my issue lies there. Can anyone tell me how to properly declare my conversion?
and here is a full version which compiles
#include <math.h>
#include <iostream>
#include <string>
#include <fstream>
using namespace std;
int main()
{
string inputfile, intstring;
float counter;
counter = 0;
float average;
average = 0;
float dividor;
dividor = 1;
cout << "Enter the name of a file to open\n";
cin >> inputfile;
ifstream inFile;
inFile.open(inputfile.c_str());
if (!inFile)
{
cout << "Error opening file " << inputfile << endl;
char stopchar;
cin >> stopchar;
return -1;
}
while (!inFile.eof())
{
//goes to next line at each space encountered in text file
getline(inFile, intstring, ' ');
cout << intstring <<","<<average<< endl;
float stringconv;
stringconv = stof(intstring);
average = (counter + stringconv)/dividor ;
dividor = dividor +1;
//increments counter to keep average output correct
}
inFile.close();
char stopchar;
cin >> stopchar;
}
Here:
string inputfile, intstring;
...
float stringconv = stof(intstring);
You can't do that. I mean, you can, but it doesn't do what you think it does. You think you're creating a macro or function or something, so that you can change intstring and then stringconv will automatically change. But what you're actually doing is converting the uninitialized string into an integer once, and never changing it again. You must do the conversion inside the read loop.
EDIT: If you're not required to use stof(), then you can save yourself a lot of headaches by using the stream input operator:
float number;
inFile >> number; // this is the basic form
while(inFile >> number) // this is how to do it as a loop
...
In C++, float stringconv = stof(intstring); won't meen automatic conversion like assign in Verilog.
Call stof() each time you need to convert.
Try this:
while (!inFile.eof())
{
getline(inFile, intstring, ' ');
cout << intstring <<","<<average<< endl;
//increments counter to keep average output correct
counter = counter +1;
//goes to next line at each space encountered in text file
stringconv = stof(intstring); // add this here
average = (counter + stringconv) /2;
}

WHILE loop and data entry

I tried earlier to use a for loop to put data in but became too problematic. So I tried to use a while loop, it works but when I tried to debug it it continued to put -858993460 into every slot. The .dat file is in the right spot and opens.
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
struct record
{
int item_id;
string item_type;
float item_price;
int num_stock;
string item_title;
string item_author;
int year_published;
};
void read_all_records(record records[], int &valid_entries);
int num_inventory_of_type(record records[], string type, int &valid_entries);
const int max_array = 100;
int main()
{
int valid_entries = 0;
record records[max_array];
read_all_records(records, valid_entries);
cout << "Stock Report" << endl;
cout << "------------" << endl;
int book = num_inventory_of_type(records, "book", valid_entries);
cout << "Book's In Stock: " << book << endl;
int cd = num_inventory_of_type(records, "cd", valid_entries);
cout << "CD's In Stock: " << cd << endl;
int dvd = num_inventory_of_type(records, "dvd", valid_entries);
cout << "DVD's In Stock: " << dvd << endl;
return 0;
}
void read_all_records(record records[], int &valid_entries)
{
ifstream invfile;
invfile.open("inventory.dat");
if (!invfile.is_open())
{
cout<<"file open failed";
exit(1);
}
while(invfile.good() && valid_entries < max_array)
{
invfile >> records[valid_entries].item_id >> records[valid_entries].item_type
>> records[valid_entries].item_price >> records[valid_entries].num_stock
>> records[valid_entries].item_title >> records[valid_entries].item_author
>> records[valid_entries].year_published;
if(!invfile.good())
break;
valid_entries++;
}
invfile.close();
}
int num_inventory_of_type(record records[], string type, int &valid_entries)
{
int count = 0;
int holder = 0;
for (int count = 0; count<valid_entries; count++);
{
if (records[count].item_type == type)
{
holder+=records[count].num_stock;
}
}
return holder;
}
the .dat file is
123456
book
69.99
16
Problem_Solving_With_C++
Walter_Savitch
2011
123457
cd
9.99
32
Sigh_No_More
Mumford_and_Sons
2010
123458
dvd
17.99
15
Red_State
Kevin_Smith
2011
123459
cd
9.99
16
The_Church_Of_Rock_And_Roll
Foxy_Shazam
2012
123460
dvd
59.99
10
The_Walking_Dead_Season_1
Robert_Kirkman
2011
all are on new lines, no spaces.
Basically it should start, run the read_all_records function and put the .dat data into the array. However, I put the cout << records[count].item_id; in the while loop just to see if the data was actually going in, and I get -858993460 each time. After that it should run the next function 3 times and return how many of each books there are.
You used the integer type int on item_price. invfile >> records[count].item_price will then only extract 69 instead of 69.99, thus resulting in a error when you try to extract the year_published.
Use a float or double instead.
struct record
{
int item_id;
string item_type;
float item_price;
int num_stock;
string item_title;
string item_author;
int year_published;
};
/* skipped identical lines */
while(invfile.good() && count < max_array)
{
invfile >> records[count].item_id >> records[count].item_type
>> records[count].item_price >> records[count].num_stock
>> records[count].item_title >> records[count].item_author
>> records[count].year_published;
cout << records[count].item_price << endl;
if(!invfile.good())
break;
cout << records[count].item_id << endl;
count++;
}
invfile.close();
Note that you have an extra semicolon in for (int count = 0; count<max_array; count++);. I guess you didn't intend this, so remove it.
This isn't a direct answer to the problem, but perhaps it will go away after a refactoring:
std::istream& operator>>(std::istream& is, record& r) {
return is >> r.item_id >> r.item_type >> … >> r.year_published;
}
int main () {
if (std::ifstream invfile("inventory.dat")) {
std::vector<record> records((std::istream_iterator<record>(invfile)),
std::istream_iterator<record>());
num_inventory_of_type(records, "dvd");
num_inventory_of_type(records, "cd");
num_inventory_of_type(records, "book");
}
}
If you still want to print out each record as you read it, the code can be rearranged as follows:
std::vector<record> records;
for (std::istream_iterator<record> i(invfile);
i != std::istream_iterator<record>(); ++i)
{
records.push_back(*i);
std::cout << i->item_id << "\n";
}
You need to change int item_price; to a float so -> float item_price;
and as mentioned above you need to swap the count++; and cout << records[count].item_id line.
After these two changes it will work properly.
struct record
{
int item_id;
string item_type;
float item_price; // <--- Needs to be a float
int num_stock;
string item_title;
string item_author;
int year_published;
};
// This order is required because you are storing in the current 'count' record and then you need to print it. Then increment the count to store the next record
cout << records[count].item_id;
count++;