getline not working as expected - c++

I'm trying to display a text file data into columns using a class structure and getline() function to read through the textfile and the dump the data into a vector class. But it seems that the program didn't even run to my 'while' loop then quitting the program. The textfile is not empty.
Below is my code:
void ScrambleWordGame::displayScoreChart() {
//open file
fstream readScoreChart("ScoreChart.txt");
string line = "";
//vector to store data in
vector<personResult> perResult;
personResult person;
//while file is open, do stuff
if(readScoreChart.is_open())
{
//check through the file
readScoreChart.seekp(0,ios::end);
//get the size of the file's data
size_t size = readScoreChart.tellg();
if(size == 0)
cout << "No results yet. Please TRY to win a game. AT LEAST~" << endl;
else
{
//create the 1st row with 4 column names
cout << left
<< setw(20) << "Player Name "
<< setw(20) << "Winning Time "
<< setw(20) << "No. Of Attempts "
<< setw(20) << "Game Level " << endl;
//fill the second line with dashes(create underline)
cout << setw(70) << setfill('-') << "-" << endl;
//read the file line by line
//push the 1st line data into 'line'
cout << getline(readScoreChart,line);
while(getline(readScoreChart,line))
{
//create stringstream n push in the entire line in
stringstream lineStream(line);
//reads the stringstream and dump the data seperated by delimiter
getline(lineStream,person.playerName,':');
getline(lineStream,person.winningTime,':');
getline(lineStream,person.noOfAttempts,':');
getline(lineStream,person.gameLvl);
//sort the results based on their timing
//sort(perResult.begin(),perResult.end(),pRes);
//display the results
cout << left
<< setfill(' ')
<< setw(25) << person.playerName
<< setw(22) << person.winningTime
<< setw(17) << person.noOfAttempts
<< setw(16) << person.gameLvl
<< endl;
}
}
}
readScoreChart.close();
}
Edit: Example of the TextFile
Joel:3:1:1
Mary:5:2:2
John:25:3:1

Your file pointer is at the end of the file after your first seek. You need to reposition it to the beginning of the file.
if(size == 0)
{
cout << "No results yet. Please TRY to win a game. AT LEAST~" << endl;
}
else
{
readScoreChart.seekp(0,ios::begin);
// all you other stuff
}

You need to seek back to the beginning of the file to be able to read. Better still, just don't seek to the end in the first place.
I'd re-structure the code a bit -- write an operator>> to read a record from a file, and an operator<< to write a record to a file.
class person {
std::string name;
std::string winning_time;
std::string num_attempts;
std::string level;
public:
bool operator<(person const &other) const {
return std::stoi(winning_time) < std::stoi(other.winning_time);
}
friend std::istream &operator>>(std::istream &in, person &p) {
std::string buffer;
std::getline(in, buffer);
std::istringstream is(buffer);
std::getline(is,p.name,':');
std::getline(is,p.winning_time,':');
std::getline(is,p.num_attempts,':');
std::getline(is,p.level);
return in;
}
friend std::ostream &operator<<(std::ostream &os, person const &p) {
return os << std::setw(25) << p.name
<< std::setw(22) << p.winning_time
<< std::setw(17) << p.num_attempts
<< std::setw(16) << p.level;
}
};
With those in place, the rest gets quite a bit simpler:
void show_header(std::ostream &os) {
cout << left
<< setw(20) << "Player Name "
<< setw(20) << "Winning Time "
<< setw(20) << "No. Of Attempts "
<< setw(20) << "Game Level " << "\n";
std::cout << std::string(70, '-');
}
void game::displayScoreChart(){
std::ifstream in("ScoreChart.txt");
// read the data:
std::vector<person> people{std::istream_iterator<person>(in),
std::istream_iterator<person>()};
if (people.empty()) {
std::cout << "No scores yet."
return;
}
// sort it by winning time:
std::sort(people.begin(), people.end());
show_header(std::cout);
// display it:
for (auto const &p : people)
std::cout << p << "\n";
return 0;
}
As a simple replacement if you don't have a C++11 compiler, an stoi can be written something like this:
int stoi(std::string in) {
return strtol(in.c_str(), NULL, 10);
}

Related

Reading and writing from a file

I need to make card catalog entries for a library system and will need a menu and save the card and read the card from a file. I'm just looking for help and not saving for anyone to do it just tips if you have any because I want ro learn and not just copy and paste.
I already finished the information they need to add but having a problem making them save and read from a file and choosing from the menu to add new entry, review entry.
#include <iostream>
#include <fstream>
#include <cstdlib>
using namespace std;
struct libary
{
string title;
string author;
string ISBN_code;
int page_count;
int publish_year;
} s[10];
bool processMenu()
{
int choice, spot;
cout << "Main Menu" << endl;
cout << "Select your options" << endl;
cout << "1. Add a new entry to the system" << endl;
cout << "2. Review an entry" << endl;
cout << "3. Save entries to a file" << endl;
cout << "4. Read entries from a file" << endl;
cin >> choice;
switch (choice)
{
case 1:
cout << "which spot do you want to add a entry to the system " << endl;
cin >> spot;
break;
case 2:
cout << "which spot do you want to review to a file " << endl;
cin >> spot;
break;
case 3:
cout << "Save entries to a file" << endl;
ofstream myfile;
myfile.open("file.txt");
myfile << "this will show in the file";
break;
case 4:
cout << "read entries from a file :" << endl;
break;
case 7:
return 0;
}
}
int main()
{
while (!processMenu())
{
cout << "Sorry, that is not a valid choice." << endl;
cout << "Please try again." << endl << endl << endl;
}
cout << " Enter information of each card catalog" << endl;
for (int i = 0; i < 10; i++)
{
cout << endl;
cout << " Enter title " << endl;
cin >> s[i].title;
cout << endl;
cout << " Enter author " << endl;
cin >> s[i].author;
cout << endl;
cout << " Enter Page count " << endl;
cin >> s[i].page_count;
cout << endl;
cout << " Enter publish year " << endl;
cin >> s[i].publish_year;
cout << endl;
cout << " Enter ISBN code, it should be 13 digits" << endl;
cin >> s[i].ISBN_code;
while (s[i].ISBN_code.length() != 13)
{
cout << " Please enter a ISBN code thats 13 digits" << endl;
cin >> s[i].ISBN_code;
cout << endl;
cout << endl;
}
}
cout << " Displaying Information" << endl;
for (int i = 0; i < 10; i++)
{
cout << " title: " << s[i].title << endl;
cout << " author: " << s[i].author << endl;
cout << " page count: " << s[i].page_count << endl;
cout << " publish year: " << s[i].publish_year << endl;
cout << " ISBN code: " << s[i].ISBN_code << endl;
}
return 0;
}
It lets me pick a spot but won't add new entry nor review or save/read.
You can make your own type writable to a stream (cout or ofstream) and/or readable from a stream (cin or ifstream) just by overriding its output (operator<<) and/or input operator (operator>>).
#include <fstream> // ifstream, ofstream
#include <iostream> // cin, cout
#include <string> // getline, string
using namespace std;
struct Card
{
// Writing to the standard output ('cout').
friend ostream& operator<<(ostream& os, const Card& card);
// Reading from the standard input ('cin').
friend istream& operator>>(istream& is, Card& card);
// Writing to a file ('ofstream').
friend ofstream& operator<<(ofstream& os, const Card& card);
// Reading from a file ('ifstream).
friend ifstream& operator>>(ifstream& is, Card& card);
string title = "";
string author = "";
string isbn = "";
int page_count = -1;
int publish_year = -1;
};
ostream& operator<<(ostream& os, const Card& card)
{
os << "Title: " << card.title << '\n'
<< "Author: " << card.author << '\n'
<< "ISBN: " << card.isbn << '\n'
<< "Page count: " << card.page_count << '\n'
<< "Publish year: " << card.publish_year << '\n';
return os;
}
istream& operator>>(istream& is, Card& card)
{
// 'getline' needs for the reading of strings because the title of a book or
// its author's name may contain Space (' ') characters. 'cin' and
// 'ifstream' separate input by every whitespace. So, if we want to allow
// spaces in our fields we have to separate them by a special delimiter
// character. We use the Newline ('\n') character here to separate fields.
// `page_count` and `publish_year` are numbers, they don't contain
// whitespaces, so they simply can be read with 'operator>>'.
cout << "Title: ";
std::getline(is, card.title);
cout << "Author: ";
std::getline(is, card.author);
cout << "ISBN code: ";
std::getline(is, card.isbn);
cout << "Page count: ";
is >> card.page_count;
cout << "Publish year: ";
is >> card.publish_year;
return is;
}
// The next two functions is for files.
ofstream& operator<<(ofstream& os, const Card& card)
{
os << card.title << '\n'
<< card.author << '\n'
<< card.isbn << '\n'
<< card.page_count << '\n'
<< card.publish_year << '\n';
return os;
}
ifstream& operator>>(ifstream& is, Card& card)
{
std::getline(is, card.title);
std::getline(is, card.author);
std::getline(is, card.isbn);
is >> card.page_count;
is >> card.publish_year;
return is;
}
int main()
{
Card card;
// Read the informations of a card from the standard input.
cin >> card;
// Write the informations of a card to the standard output.
cout << card;
// Write the informations of a card to "file.txt".
ofstream ofs("file.txt");
ofs << card;
ofs.close();
// Read the informations of a card from "file.txt".
ifstream ifs("file.txt");
ifs >> card;
ifs.close();
return 0;
}
Important: For the sake of simplicity, I omitted error checking at the opening of a file and at the writing/reading of a stream. In your code you should do these.

C++ Dynamic array of structs segmentation fault

I am working on a program that reads data from a text file called "phonebook.txt" and displays it in terminal. It ask how many contacts the user wants to add. The user will input the number of contacts he/she wants and the program will output this along with the old contacts from "phonebook.txt" to "updated-phonebook.txt." Now I chose to use a dynamic array over a vector simply because I wanted to get a feel for how to handle memory allocation.
#include <iostream>
#include <string>
#include <fstream>
#define nullptr 0;
struct Person
{
std::string lName, fName, streetAddr, city, state, zip, phoneNum;
};
int main ()
{
std::ofstream outFile;
std::string dummy;
int amount, total, count = 0;
Person *contact;
//set the pointer to 0 so its not pointing
//to anything else in memory
contact = nullptr;
std::ifstream inFile("/home/isemanthaj/My_Programs/Class_Programs/Assignment_3/phonebook/phonebook.txt",std::ios::in);
if (!inFile)
{
std::cout << "File failed to open" << std::endl;
}
else
outFile.open("updated_phonebook.txt", std::ios::out);
std::cout << "How many contacts do you want to add?" << std::endl;
std::cin >> amount;
contact = new Person[amount];
std::cout << "Contacts in the previous book \n" << std::endl;
std::cin.ignore();
while (inFile)
{
//dummy stores the contact number which is the first line
// in the read file. I don't want to carry the number over
// to the other updated_phonebook.txt file
std::getline(inFile, dummy);
std::getline(inFile, contact[count].lName);
std::getline(inFile, contact[count].fName);
std::getline(inFile, contact[count].streetAddr);
std::getline(inFile, contact[count].city);
std::getline(inFile, contact[count].state);
std::getline(inFile, contact[count].zip);
std::getline(inFile, contact[count].phoneNum);
std::cout << contact[count].lName << std::endl;
std::cout << contact[count].fName << std::endl;
std::cout << contact[count].streetAddr << std::endl;
std::cout << contact[count].city << std::endl;
std::cout << contact[count].state << std::endl;
std::cout << contact[count].zip << std::endl;
std::cout << contact[count].phoneNum << std::endl;
outFile << contact[count].lName << std::endl;
outFile << contact[count].fName << std::endl;
outFile << contact[count].streetAddr << std::endl;
outFile << contact[count].city << std::endl;
outFile << contact[count].state << std::endl;
outFile << contact[count].zip << std::endl;
outFile << contact[count].phoneNum << std::endl;
count++;
}
// I know this is a little wacky here.
// I want the program to display the total amount of contacts
// to the screen
total = amount + count;
for (int index = 0; index < total; index++)
{
std::cout << "Last name: ";
std::getline(std::cin, contact[index].lName);
std::cout << "First name: ";
std::getline(std::cin, contact[index].fName);
std::cout << "Street address: ";
std::getline(std::cin, contact[index].streetAddr);
std::cout << "City: ";
std::getline(std::cin, contact[index].city);
std::cout << "State: ";
std::getline(std::cin, contact[index].state);
std::cout << "Zip code: ";
std::getline(std::cin, contact[index].zip);
std::cout << "Phone number: ";
std::getline(std::cin, contact[index].phoneNum);
std::cout << std::endl;
std::cout << "Contact: " << index + 1 << std::endl;
std::cout << "Last name: " << contact[index].lName << std::endl;
std::cout << "First name: " << contact[index].fName << std::endl;
std::cout << "Street address: " << contact[index].streetAddr << std::endl;
std::cout << "City: " << contact[index].city << std::endl;
std::cout << "State: " << contact[index].state << std::endl;
std::cout << "Zip code: " << contact[index].zip << std::endl;
std::cout << "Phone number: " << contact[index].phoneNum << std::endl;
std::cout << std::endl;
outFile << "Last name: " << contact[index].lName << std::endl;
outFile << "First name: " << contact[index].fName << std::endl;
outFile << "Street address: " << contact[index].streetAddr << std::endl;
outFile << "City: " << contact[index].city << std::endl;
outFile << "State: " << contact[index].state << std::endl;
outFile << "Zip code: " << contact[index].zip << std::endl;
outFile << "Phone number: " << contact[index].phoneNum << std::endl;
}
inFile.close();
outFile.close();
delete [] contact;
return 0;
}
I am trying to store the new contacts the user created and the old contacts from the read file in one dynamic array of structures. Am I getting this segmentation fault because I am using two different indexes "count" and "index" to store the contacts in the same dynamic array?
Your segfault is happening because you are trying to access an out-of-bounds element of contact array. The size of contact is amount, and you are iterating it from 0 to amount + count. Obviously, amount + count >= amount, so sooner or later you will run out of bounds.
I would suggest you to use std::vector instead of a plain array. You will always be aware of it's size and will be able to iterate it safely.
If you want to keep the arrays, you will have to either reallocate contact after copying the contacts from the old file, to make it size equal to total, or make two separate arrays: one to hold the records from the old file with the size of amount elements, and another one for the newly-added contacts with the size of count elements.
As it is mentioned in the comments, your end-of-file check is wrong, this and this questions are relevant.

Using C++ stream for cout or text file

I have a very simple program where I ask the user if they want to print to screen or a file. Rather than create two sets of output sections, I thought I could switch a stream to either cout or an ofstream and then output to that stream. However, I'm getting screen output no matter what.
ostream &out = cout;
do
{
cout << "Write to file (f) or screen (s)?";
cin >> yes_or_no;
} while (yes_or_no != 'f' && yes_or_no !='s');
if (yes_or_no=='f')
{
ofstream out;
out.open("Report.txt");
cout << "Writing report to Report.txt" << endl;
system("pause");
}
out << "Day: Current Value ROI" << endl;
out << "------------------------------------------" << endl;
out << setw(5) << 0;
out << "$" << setw(20) << setprecision (2) << fixed << initial_value;
out << setw(12) << "1.00" << endl;
for (int day = 1 ; day < number_of_days ; day++)
{
current_value = generateNextStockValue(current_value, volatility, trend);
out << setw(5) << day;
out << setw(20) << setprecision (2) << fixed << current_value;
out << setw(12) << setprecision (2) << fixed << current_value / initial_value;
out << endl;
}
You could put all the writing logic inside a function, and let the caller decide which output stream to write to:
void do_the_stuff(std::ostream& os)
{
// write to os
os << "blah blah" ....
}
then
if (yes_or_no=='f')
{
ofstream out("Report.txt");
do_the_stuff(out);
} else {
do_the_stuff(std::cout);
}

Print function to an output file

I am about to be finished with a program I am writing and have reached a roadblock.
I am trying to print the contents of a function called print which is called by a pointer.
My problem is I need to print the contents of the function to an output file and am not sure how.
This is my print function:
void English::Print(){
int formatlength = 38 - (static_cast<int>(firstName.size() + lastName.size()));
cout << firstName << " " << lastName;
cout << setw(formatlength) << finalExam;
cout << setprecision(2) << fixed << setw(11) << FinalGrade();
cout << setw(4) << Lettergrade() << endl;
}
This is the implementation of the print function:
for (int i = 0; i <= numStudents - 1; i++) {
if (list[i]->GetSubject() == "English") {
list[i]->Print();
}
}
Where the for loop is cycling through my list of Students.
My goal is that the list[i]->Print() will print to my output file.
Simply replace cout with an ostream object, something like :
void English::Print(ostream& fout){
//ofstream of("myfile.txt", std::ios_base::app);
int formatlength = 38 - (static_cast<int>(firstName.size() + lastName.size()));
fout << firstName << " " << lastName;
fout << setw(formatlength) << finalExam;
fout << setprecision(2) << fixed << setw(11) << FinalGrade();
fout << setw(4) << Lettergrade() << endl;
}
Also, you can overload << operator too in your class English
friend ostream& operator <<( ostream& os, const English& E )
{
//
return os;
}
And then can simply use:
fout << list[i] ;
Besides the answers above, I think you should try this way, using the C's original file redirection function:
Put this instruction in the first line of your main function:
int main(){
freopen("out.txt", "w", stdout);
//your codes
The "out.txt" is the file you wanna to put the data in, "w" means you want to write in the file, and stdout is the standard output stream that has been redirected.

Sequential file initialization unexpected behavior

I'm trying to initialize a file with 100 empty records with the code below:
void initializeInventory() {
std::ofstream out("hardware.dat", std::ios::binary);
Hardware h;
for (int i = 0; i < 100; ++i) {
h.ID = i;
h.name = "try"; // std::string();
h.quantity = 0;
h.price = 0;
h.notes = "try2"; //std::string();
out.write(reinterpret_cast<const char*>(&h), sizeof(Hardware));
}
out.close();
}
But when I try to print them out, it always print only 25 elements or crashes.
This is the function to print out elements:
void readInventory() {
std::ifstream in("hardware.dat", std::ios::in);
std::cout << std::setiosflags(std::ios::left) << std::setw(4) << "ID"
<< std::setw(16) << "Name"
<< std::setw(11) << "Quantity"
<< std::setw(10) << std::resetiosflags(std::ios::left)
<< "Price"
<< std::setw(50) << "Notes" << '\n';
Hardware h;
while (!in.eof()) {
in.read(reinterpret_cast<char*>(&h), sizeof(Hardware));
//if (!in.eof())
printOut(std::cout, h);
}
in.close();
}
void printOut(std::ostream &output, const Hardware& h) {
output << std::setiosflags(std::ios::left) << std::setw(4) << h.ID
<< std::setw(16) << h.name
<< std::setw(11) << h.quantity
<< std::setw(10) << std::setprecision(2)
<< std::resetiosflags(std::ios::left)
<< std::setiosflags(std::ios::fixed | std::ios::showpoint )
<< h.price
<< std::setw(50) << h.notes << '\n';
}
I also noted that if I increase the number of cycles in the for (I tried putting 400 instead of 100) the file hardware.dat seems to grow, so I thought that should be a problem in the print function. Any idea? Is there something I'm missing?
Thanks in advance.
It would be the best to overload the operator <<
friend ostream& operator<<(ostream& out, const Hardware& h) // output
{
out << "(" << h.id() << ", " << h.whatever() << ")";
return out;
}
and read the file line by line to the Hardware object.
By the way, overloading the input operator >> is also possible.