Runtime error, possible input problem? - c++

I have a book class that takes title, author, copyright, ISBN number, and checkout for a book object. However, I'm getting a runtime error when the program runs. After the user inputs the title and presses enter, the program skips down, showing the rest of the outputs, and then terminates the program giving a runtime error.
I tried to catch an exception, but I didn't get anything.
Code:
#include "std_lib_facilities.h"
class Book{
public:
string what_title();
string what_author();
int what_copyright();
void store_ISBN();
void is_checkout();
private:
char check;
int ISBNfirst, ISBNsecond, ISBNthird;
char ISBNlast;
string title;
string author;
int copyright;
};
string Book::what_title()
{
cout << "Title: ";
cin >> title;
cout << endl;
return title;
}
string Book::what_author()
{
cout << "Author: ";
cin >> author;
cout << endl;
return author;
}
int Book::what_copyright()
{
cout << "Copyright Year: ";
cin >> copyright;
cout << endl;
return copyright;
}
void Book::store_ISBN()
{
bool test = false;
cout << "Enter ISBN number separated by spaces: ";
while(!test){
cin >> ISBNfirst >> ISBNsecond >> ISBNthird >> ISBNlast;
if((ISBNfirst || ISBNsecond || ISBNthird)<0 || (ISBNfirst || ISBNsecond || ISBNthird)>9)
error("Invalid entry.");
else if(!isdigit(ISBNlast) || !isalpha(ISBNlast))
error("Invalid entry.");
else test = true;}
}
void Book::is_checkout()
{
bool test = false;
cout << "Checked out?(Y or N): ";
while(!test){
cin >> check;
if(check = 'Y') test = true;
else if(check = 'N') test = true;
else error("Invalid value.");}
}
int main()
{
Book one;
one.what_title();
one.what_author();
one.what_copyright();
one.store_ISBN();
one.is_checkout();
keep_window_open();
}
Not sure what the problem could be. Any help is appreciated, thanks.
Output example:
Title: One Flew Over the Cuckoo's Nest
(next lines aren't actually spaced in between and all output at once)
Author:
Copyright Year:
Enter ISBN number separated by spaces:
This application has requested the Runtime to terminate it in an unusual way. Please contact support for more information.
Also don't worry about the keep_window_open and error functions. They are part of the std_lib_facilities.h and are most likely not causing the problem. Error just outputs an error message if a problem is encountered.

The problem here is that the C++ input streams do not remove malformatted input that they encounter. In other words if you try to read in a number and the stream contains, for example the character 'x' (not a number), that character isn't removed from the input stream. Additionally, if I remember correctly, that will also put the input stream in an error state causing well-formatted input to also fail. Although there is a mechanism for testing the state of the input stream and removing malformattted input and clearing the error flags, I have personally found it simpler to always read into a string (using ">>" or "getline") and then to parse the string. In the case of a number, for example, you can use the "strtol" or "strtoul" functions.

Related

Program skipping first input after loop

I'm working on this code which is asking user to input data (student data).
I made a constructor which is taking input of all fields on first run which goes fine on the first run.
When I continue to go again via loop its skipping the Name input every time.
Code
#include<iostream>
#include<fstream>
#include<string>
using namespace std;
class person{
private:
string sName;
string fName;
string sAddress;
public:
person(){
cout << "Enter Details\n";
cout << "Name :";
getline(cin,sName);
cout << "Father's Name :";
getline(cin,fName);
cout << "Address :";
getline(cin,sAddress);
cout << sName << fName << sAddress;
ofstream myfiles("person.txt",ios::app);
myfiles<<sName<<endl;
myfiles<<fName<<endl;
myfiles<<sAddress<<endl;
myfiles.close();
checkLoop();
}
void checkLoop(){
int choice{};
cout << "\nDo you want to continue?\n1)Continue\tx)Anything else for exit!"<<endl;
cin >> choice;
switch(choice){
case 1:
person();
//break;
default:
break;
}
}
};
int main()
{
person p1;
return 0;
}
Output:
Do you want to continue?
1)Continue x)Anything else for exit!
1
Enter Details
Name :Father's Name :
When I choose the first option Continue, it's skipping the "Name" part.
This is happening due to the fact that when you are reading choice using cin, then it will read the choice leaving the newline character behind.
That’s why when you try to read name by getline() it reads endofline character (Due to this in the person.txt you will see there will be blank spaces in name part).
So just use cin.ignore() after cin>>choice;

My if statement isn't printing what I need it to in my while loop

#include <iostream>
#include <string>
#include <fstream>
using namespace std;
int main() {
bool exit = true;
bool wrongssn = false;
string menu;
cout << "To add a record, type add.\n";
cout << "To find someone by SSN, type find by ssn\n";
cout << "to display all records, type display all\n";
while (exit == true) {
cin >> menu;
if (menu == "add") {
cout << "ye";
}
if (menu == "find by ssn") {
cout << "Type the SSN you wish to search by";
int ssn;
cin >> ssn;
if (!cin) {
cout << "invalid choice, retry.\n";
}
}
im trying to get my second if statement to print out, but when I run the program, if I type "find by ssn", it will not print anything and keep asking me for keyboard input until I manually close out of the program.
This is because when you do cin>>menu, then menu stores find and not find by ssn because taking string input using cin stops after it encounters a whitespace. Instead use std::getline.
Syntax:
std::getline(std::cin, menu);

Class holding vector of classes

I am learning OOP in C++ so I tried to practice a little. I want to create a "database" (using objects) of class and its students. I created a class for a student. Each have a name and age.
class Ziak {
public:
Ziak(int,string);
int getAge() {
return age;
}
string getName() {
return name;
}
private:
int age;
string name;
};
Ziak::Ziak(int age,string name) {
this->age=age;
this->name=name;
}
And I created a class of a Classroom , each is represented by its name , teacher and students ( vector of objects )
class Trieda {
public:
Trieda(string,string);
void fillStudents() {
while(1) {
int age;
string name;
cout << "Name: ";
getline(cin,name);
cout << "Age: ";
cin >> age;
Ziak newStudent(age,name);
n.push_back(newStudent);
if(cin.eof()) {
break;
}
}
}
string getTeacher() {
return ucitel;
}
string getNamesOfStudents() {
for(unsigned i = 0; i < n.size(); i++) {
cout << "hey " << n[i].getName() << endl;
}
}
protected:
vector<Ziak> n;
string nazov;
string ucitel;
};
Trieda::Trieda(string nazov,string ucitel) {
this->nazov = nazov;
this->ucitel = ucitel;
}
and I am just calling its methods in main:
int main() {
Trieda newClass("4A","Ms Foster");
newClass.fillStudents();
newClass.getNamesOfStudents();
return 0;
}
My problem here is the method fillStudents() output starts pretty nice:
Name: // insert name
Age : // insert age
but the second iteration looks worse:
Name:Age: // insert something
and the third iteration is an infinite loop printing Name:Age: till the end of the world.
I tried to just use cin >> name instead of getline, e.g:
void fillStudents() {
while(1) {
int age;
string name;
cout << "Name: ";
cin >> name;
/*getline(cin,name);*/
cout << "Age: ";
cin >> age;
if(cin.eof()) {
break;
} else {
Ziak newStudent(age,name);
n.push_back(newStudent);
}
cin.clear();
}
}
It works. But it works like this:
Name: // insert name
Age : // insert age
// it works till the last one when i press ctrl+Z as eof (currently on windows)
// it outputs Age: Hey + first name ....
Hey name 2 // and so on , then it crashes
What causes this? I suspect it could be something with buffer not being cleared but I tried cin.clear() and it happened too. How can I implement it with getline (I wanted full name as name input e.g John Wicked).
I am trying to figure it out but I'm just a beginner with C++ so my knowledge is limited.
Edit
I fixed it using cin.get() as advised from question posted in comments, now my function looks like this:
void fillStudents() {
while(1) {
int age;
string name;
cout << "Name: ";
//cin >> name;
getline(cin,name);
cout << "Age: ";
cin >> age;
if(cin.eof()) {
break;
} else {
if(cin.fail()) {
cout<< "chyba ";
break;
}
Ziak newStudent(age,name);
n.push_back(newStudent);
}
cin.get();
}
}
What still makes me uncomfortable is the output with cout. It produces this:
Name: John
Age: 15
Name: Lia
Age: 25
Name: Age: hey John
hey Lia
The Name: Age: still gets printed after eof, I tried to put cin.eof() test at the start of the while loop but it messed up all of the output. How can I modify the code without it showing the cout after eof?
You need to check for eof (Ctrl+z(^Z)) after name has been entered and after age has been entered. If you enter Ctrl+z for name then the eofbit flag will be set on cin but not caught until after outputting Age:. The reason it doesn't pause for age input is because:
The extraction also stops if the end of file is reached in [the istream] or if some other error occurs during the input operation. - C++ Reference - getline
Note: The istream in the quote above is cin in your code.
Your code should resemble:
// above code removed for brevity
cout << "Name: ";
getline(cin,name);
if(cin.eof()) {
break;
}
cout << "Age: ";
cin >> age;
if(cin.eof()) {
break;
} else {
// below code removed for brevity
Output:
Tested on Windows with CodeBlocks and GCC.
Name: ^Z[Enter]
Process returned 0 (0x0) execution time : 1.685 s
Press any key to continue.
Name: John
Age: ^Z [Enter]
Process returned 0 (0x0) execution time : 1.685 s
Press any key to continue.

Input validation for certain test not working as expected

double checkInput() {
double add;
cout << "\n" << endl;
cin >> add;
if (cin.fail()==true)
{
cin.clear();
cin.ignore(INT_MAX, '\n');
cout << "Incorrect input"<<endl;
}
else
{
return add;
}
}
I use this bit of code to filter out character inputs eg "Blarg","bat12cat" and similar inputs where the character/letter come first but when i test with "1gold" ,"0.05cake" etc where number comes first then letters,the program accepts the all numbers up to the first instance of a letter.
My understanding is that it is the cin.ignore() that is causing the issue and is allowing the numbers through.
What would let inputs like"0.05Cats" be ignored/skipped altogether?.
Searching online,people suggest using getline() and stringstream.
Thank you.
When you input something like 1.5dog and then use cin >> some_double; >> is going to extract out a double until it can't read any more. So some_double gets the 1.5 and dog is still in the stream. This is not a failure and as such the failbit is not set. Since it is not set you skip your if statement and return the double value while the rest of the input stays in the stream and will cause you issues the next time you try to read from the stream. My suggestion is to change how you read your inputs. You can take in the input via a std::string and then convert it to the desired type. Then if the conversion fails you you can signal that you had a failure. I would use something like:
bool read_double(std::istream & is, double & number)
{
std::string line;
getline(is, line);
std::size_t pos = 0;
double temp = stod(line, &pos);
if (pos != line.size()) // there is extra content in the streams
return false;
number = temp;
return true;
}
And you can see it working with this Live Example
Usually there is more than one way to do the right thing. Using "c++(11) regex class object" would help you. You can edit regex for your needs (for example to include hex numbers by adding -a,b,c,d,e,f).
#include <iostream>
using namespace std;
#include <string>
using std::string;
#include <regex>
void main()
{
string input;
std::regex reg("[-+]?([0-9]*\.[0-9]+|[0-9]+)");
cout << "Please enter a double type\n";
while (cin >> input)
{
if (std::regex_match(input, reg))
{
std::cout << "Valid input\n";
break;
}
else
{
cout << "Invalid input\n";
cout << "Please enter a double type\n";
}
}
double do_with = std::stod(input,NULL);
cout << "the double is : " << do_with << endl;
cin >> input; // to hold the shell
}

While loop fail, when reading int and string C++

I'm trying to read multiple Strings and ints from the user. I want to take pairs of Name and Age and keep doing it until the user types in "done". But my do-while crashes early on, and i can't figure out why?
int number;
string name;
do
{
cout << "Your name: " ;
getline(cin, name);
cout <<name<< " age: " ;
cin >> number;
}
while (name!="done");
Edit: Also after entering "done", i have to enter "done" also on age, why is that?
I tried to run your program in VS 2010, and even when I entered a valid number, the program would, to my surprise skip reading the next name.
It seems to me that cin >> number doesn't swallow the '\n' I naturally entered after the number.
I attempted adding a call to getchar() after each cin >> number and the program suprisingly started working as expected.
So the conclusion is, that you should clean()/ignore() after cin >> number even after the number entered was OK, or resort to using getline() (and then parsing) for reading numbers.
If you want not to input "done"'s age then you have to break out of the loop immediately after it's entered. My final code:
#include <iostream>
#include <string>
using namespace std;
int main()
{
int number;
string name;
while(true)
{
cout << "Your name: " ;
getline(cin, name);
if(name == "done")
break;
cout <<name<< " age: " ;
cin >> number;
cin.clear();
cin.ignore(std::numeric_limits<streamsize>::max(),'\n');
}
}
If someone enters an invalid age, i.e. something that isn't a number, you need to clear the flags of your cin, and also flush any remaining characters.
if( !(cin >> number )
{
cin.clear();
}
cin.ignore( std::numeric_limits<streamsize>::max() );
If it does read a number then you also need to ignore the newline after it.
Use the std namespace
This way
int number;
string name;
do
{
std::cout<<"Your name: " ;
std::cin>>name;
std::cout<<name<<" age: " ;
std::cin>>number;
}
while (name!="done");
Or this
using namspace std;
int number;
string name;
do
{
std::cout<<"Your name: " ;
std::cin>>name;
std::cout<<name<<" age: " ;
std::cin>>number;
}
while (name!="done");
#include <iostream>
#include <string>
int main ()
{
std::string name;
int age;
while (true)
{
std::cout << "Please enter your name: ";
std::cin >> name;
if ( "done" == name )
break;
std::cout << name << ", please enter your age: ";
std::cin >> age;
std::cout << name << ", you are " << age << " years old\n";
}
std::cout << "Bye.\n";
return 0;
}
Mixing use of getline() and >> can be problematic. Best to just avoid it if you can. You could also use getline() for both, and convert the int using a stringstream, or possibly atoi(), but I don't like that much.
Because you give it a string while it expect an int as age.
At the end of the first iterate, as you typed the Enter key to input the age, an extra newline was taken into the cin. So in the second iterate, the newline showed up as an empty line, causing the name to be set as an empty string. Then the program wanted an int as age. If you are not careful enough, you would input a string and cause the program crash.
One suggestion:
To replace:
getline(cin, name);
with:
do {
getline(cin, name);
} while (name.empty());