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.
Related
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;
I'm trying to look for what is wrong in this fragment of code. It says the error : [Error] no match for 'operator>>' in 'inputData >> Player[i].AthleteType::firstName' for the line:
inputData >> Player[i].firstName;
Can someone tell me what this means? And also if this is the right way to read data from a file that looks like this:
Peter Gab 2653 Kenya 127
Usian Bolt 6534 Jamaica 128
Other Name 2973 Bangladesh -1
Bla Bla 5182 India 129
Some Name 7612 London -1
//this is the structure
struct AthleteType
{
string firstName[SIZE];
string lastName[SIZE];
int athleteNumber[SIZE];
string country[SIZE];
int athleteTime[SIZE];
};
void readInput(int SIZE)
{
AthleteType Player[SIZE];
ifstream inputData("Athlete info.txt");
int noOfRecords=0;
for (int i=0; i < SIZE; i++, noOfRecords++)
{
inputData >> Player[i].firstName;
inputData >> Player[i].lastName;
inputData >> Player[i].athleteNumber;
inputData >> Player[i].country;
inputData >> Player[i].athleteTime;
}
for (int i=0; i < noOfRecords; i++)
{
cout << "First Name: " << Player[i].firstName << endl;
cout << "Last Name: " << Player[i].lastName << endl;
cout << "Athlete Number: " << Player[i].athleteNumber << endl;
cout << "Country: " << Player[i].country << endl;
cout << "Athlete Time: " << Player[i].athleteTime << endl;
cout << endl;
}
}
There are several problems with your attempt. Firstly your struct
struct AthleteType {
string firstName[SIZE];
string lastName[SIZE];
int athleteNumber[SIZE];
string country[SIZE];
int athleteTime[SIZE];
};
Your compiler error is telling you that you can't read into an array of strings, inputData >> firstName[SIZE];. One string at a time is fine of course.
If i peer into my crystal ball, I see that you want to store several athletes. This should be done using a vector.
vector<Athlete> athletes;
And the struct can then be
struct Athlete
{
string firstName;
string lastName;
int athleteNumber;
string country;
int athleteTime;
};
One athlete per object.
When reading from an input file you want to read based on read success.
while(inputData >> athlete){
athletes.push_back(athlete);
}
You can do this by overloading operator>> (istream&, Athlete& ); or you can write a function that does a similar job.
istream& readAthlete(istream& in, Athlete& at){
return in >> at.firstName >> at.lastName >> at.athleteNumber >> ... and so on;
}
Now the read function can be written as
vector<Athlete> readInput(string filename){
vector<Athlete> athletes;
ifstream inputData(filename);
Athlete athlete;
while(readAthlete(inputData, athlete)){
athletes.push_back(athlete);
}
return athletes;
}
This is not tested code, it might work, it might not work, but it should give you a reasonable path forward.
So I want to validate the user only enters text and not numbers. If any number is input, then I ask her again for input. I thought this would do the trick, but it doesn't seem to work:
#include <iostream>
using namespace std;
int main()
{
string name = "";
cout << "Enter name: ";
getline(cin, name);
while (!cin) // or cin.fail()
{
cout << "Numbers are not allowed, input name again: ";
cin.clear();
cin.ignore(1000, '\n'); // is this even necessary since getline() already consumes spaces and new lines?
getline(cin, name);
}
}
Because the name variable is of string type, shouldn't the cin object fail when it receives a number? How can I validate it and make sure it prompts for input again when a number is entered? Also, out of curiosity since I'm asking already, if the user enters something like: Scarlett9356, what would be a good way to re-prompt for good input? Thank you.
You could validate that there are no numbers in your string by doing this:
#include <iostream>
using namespace std;
bool validName(string name)
{
if(name.length() == 0)
return false; // `name` cannot be empty
for(int i = 0; i < name.length(); i++)
if(name[i] >= '0' && name[i] <= '9')
return false; // There is a number in `name`
return true; // `name` is valid
}
int main()
{
string name = "";
cout << "Enter name: ";
getline(cin, name);
while (!validName(name))
{
cout << "Numbers are not allowed, input name again: ";
cin.clear();
cin.ignore(1000, '\n'); // is this even necessary since getline() already consumes spaces and new lines?
getline(cin, name);
}
}
Because the name variable is of string type, shouldn't the cin object fail when it receives a number?
No. Input that consists of digits is valid as string also.
You'll need to use a different strategy for making that an invalid input.
I would suggest something along the lines of:
bool is_valid_input(std::string const& name)
{
bool is_valid = true;
// Figure out the logic for deciding when the input is not valid.
// ...
if (!is_valid )
{
cout << "Numbers are not allowed, input name again: ";
}
return is_valid;
}
int main()
{
string name = "";
do
{
cout << "Enter name: ";
getline(cin, name);
}
while ( !is_valid_input(name) );
}
If you want to limit your input to only taking in string without a number in it then you can use std::any_of and std::isdigit
std::string name = "";
std::cout << "Enter name: ";
std::getline(std::cin, name);
while (std::any_of(name.begin(), name.end(), [](char ch) { return std::isdigit(ch); }))
{
std::cout << "Numbers are not allowed, input name again: ";
std::getline(std::cin, name);
}
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());
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.