I tried to input string inside the function, getline(cin, worker->Name) works for the first time input but for the next input it skips or got ignored. it works for the integer but it doesn't work for the string, what should I do?
Code:
#include <iostream>
using namespace std;
struct Worker {
string Name;
int Salary;
int Status;
int Child;
};
void InputWorkerData(Worker *worker) {
cout << "Nama: ";
getline(cin, Worker->Name);
cout << "Gaji per bulan: ";
cin >> worker->Salary;
cout << "status (menikah = 1, single = 0): ";
cin >> worker->Status;
if(worker->Status == 1) {
cout << "jumlah anak: ";
cin >> worker->Child;
} else {
worker->Child = 0;
}
cout << endl;
}
int main() {
Worker worker1, worker2, worker3;
InputWorkerData(&worker1);
InputWorkerData(&worker2);
InputWorkerData(&worker3);
return 0;
}
Output:
Nama: michael jordan
Gaji per bulan: 7000
status (menikah = 1, single = 0): 1
jumlah anak: 3
Nama: Gaji per bulan: 5000
status (menikah = 1, single = 0): 0
Nama: Gaji per bulan: 9000
status (menikah = 1, single = 0): 1
jumlah anak: 2
Mixing line-oriented and item-oriented input can (and often will) lead to problems like this.
This seems to fit the typical case: after reading an item (in this case a number) using >>, there's a new-line character still sitting in the input buffer. When you call getline, it sees that new-line character as the end of an otherwise empty line.
There are a few ways to avoid this problem. One common, well-known one is to use getline to read input a line at a time throughout, then use (for one possibility) a lexical_cast to convert the data from a string to the type you actually wanted to read.
use fflush(stdin) after each input i.e after cout and before getline() .It will solve your problem.you have to flushout the buffer first.
Related
What's the benefit of converting the variable from string to int using std::stoi?
I saw a lecture and it declares string variable and converts it to int later. Any reason for doing this? or it's just a preference?
I don't think using stoi in the given code is necessary.
#include <iostream>
int main() {
std::string sAge = "0";
int nGrade = 0;
std::cout << "Enter age: ";
getline (std::cin, sAge);
int nAge = std::stoi(sAge);
if ((nAge >= 1) && (nAge <= 18)) {
std::cout << "Important Birthday" << std::endl;
}
else if ((nAge == 21) || (nAge == 50)) {
std::cout << "Important Birthday" << std::endl;
}
else if (nAge >= 65) {
std::cout << "Important Birthday" << std::endl;
}
else {
std::cout << "Not an important Birthday" << std::endl;
}
return 0; }
There are a couple of reasons people use getline, then convert the string to an int with std::stoi instead of extracting an int directly from the stream (like std::cin >> nAge;).
The first is that it can be a little bit easier to recover from errors this way. If you try to extract an int directly from the stream, but the user enters six instead of 6, the stream will go into a failed state, and you'll need to extract the six from the stream and reset the stream before you can try again.
If you read a line, then use stoi to do the conversion, it's fairly easy to just read a line repeatedly until you get something can convert successfully.
It can also be somewhat clumsy if you try to mix reading things a line at a time with reading things an item at a time. For example:
std::cout << "Please enter your age: ";
std::cin >> age;
std::cout << "Please enter your name: ";
std::getline(std::cin, name);
Assuming the user enters data roughly as intended, we can expect this to read the name as an empty string. The problem is that when we read the age, it read the digits and converted them to age. But that was followed by a new-line character, which hasn't been read yet--so when we call getline, it will see the new-line, and treat that as the end of the line, so name ends up empty. There are ways to avoid/fix this but they add a bit of clumsiness, so sometimes it's easier to just read all the data with getline, and convert it as needed afterwards.
i'm trying to make the c++ program that manage student list but got into error from the very beginning. Here's my program:
#include<iostream>
#include<string.h>
using namespace std;
struct Candidate
{
char id[5];
char fullname[30];
int reading, listening,total;
};
int main ()
{
struct Candidate can[100];
int n=0;
do {
cout << "Input number of candidate:";
cin >> n;
if (n <= 0 || n>=50)
cout << "Candidate number must be between 0 and 50:\n";
} while (n <= 0 || n>=50);
for (int i = 0; i < n; i++)
{
cout << "Input information for candidate number " << i + 1 << endl;
cin.ignore(32323, '\n');
cout << "Input ID(only 5 character):";
gets(can[i].id);
cout << "Input full name:";
gets(can[i].fullname);
do {
cout << "Input reading mark:";
cin >> can[i].reading;
if(can[i].reading < 5 || can[i].reading>495)
cout<<"Your reading mark is not between 5 and 495\n";
} while (can[i].reading < 5 || can[i].reading>495);
do {
cout << "Input listening mark:";
cin >> can[i].listening;
if(can[i].listening < 5 || can[i].listening>495)
cout<<"Your listening mark is not between 5 and 495\n";
} while (can[i].listening < 5 || can[i].listening>495);
can[i].total = can[i].reading + can[i].listening;
}
cout << endl << can[0].id<<endl;
}
So i got an output like this:
Input number of candidate:1
Input information for candidate number 1
Input ID(only 5 character):EW2RR
Input full name:Test1
Input reading mark:344
Input listening mark:233
EW2RRTest1
It seems like the value of fullname is written continually to ID. I have tried a lot of way to fix but couldn't figure out. Does anyone have a clue?
In every string if you have a string length of N you must have char array of size at least N+1 for '\0' which indicates that the string ends here for cout to stop printing.
In your case you declared char array of size 5 and you fill all 5 with characters so '\0' is put somewhere else. Notice that "id" and "fullname" locate next to each other , so my best guess is that gets put '\0' of when you scan "ID" which should be "id[5]" to "fullname[0]", and then when you scan "FULLNAME" , it replaces this '\0' , so "id" has no termination point and must use termination point of "fullname". That's why it seems like fullname has been appended to id. Be aware that this is not the default behavior , the program may act different on other machines.
Also , gets() is a broken function , if you use cin or scanf before , you should flush your stdin first by calling
fflush(stdin);
before you use gets() because sometimes '\n' is left in the stdin but in your case this was taken care by
cin.ignore(32323, '\n');
Using fgets() as Paul Rooney stated is much more preferable. I myself also had a lot of problems with gets.
Happy Coding!!
You need to put a terminal '\0' char at the end of every string.
When the ID gets printed, the code (cout) won't stop til it hits a NULL.
This is called overrunning your buffer.
I tried to run the following code but after one input, the rest of the input is initialized to zero and is displayed on the screen automatically. where did I go wrong?
#include<iostream>
#define N 50
using namespace std;
struct movies_t
{
char title[60];
int year;
}user[N];
void printmovie(movies_t);
int main()
{
for(int i = 0; i < N; i++)
{
cout << "Enter title: ";
cin.get(user[i].title, 60);
cout << "Enter year: ";
cin >> user[i].year;
}
cout << "\nYou have entered these movie: \n";
for(int i = 0; i < N; i++)
printmovie(user[i]);
return 0;
}
void printmovie(movies_t m)
{
cout << m.title;
cout << " (" << m.year << ")\n";
}
The problem is that when you press enter after inputting the year for the first movie, that enter (newline) is still in the input buffer, so when you next call cin.get(...) it will read that newline and think you entered an empty line.
You need to tell cin to ignore the rest of the line including the newline.
After execution of
cin >> user[i].year;
the newline character is still left in the input stream.
The next time you execute
cin.get(user[i].title, 60);
the first character encountered is the newline character. When that happens, failbit of cin is set. Here's some info from http://en.cppreference.com/w/cpp/io/basic_istream/get (emphasis mine):
4) Reads characters and stores them into the successive locations of the character array whose first element is pointed to by s. Characters are extracted and stored until any of the following occurs:
n-1 characters have been stored
end of file condition occurs in the input sequence (setstate(eofbit) is called)
the next available input character c equals delim, as determined by Traits::eq(c, delim). This character is not extracted (unlike basic_istream::getline())
If no characters were extracted, calls setstate(failbit). In any case, if count>0, a null character (CharT() is stored in the next successive location of the array.
After the failbit of cin is set, nothing is read from cin until the state is cleared by an explicit call to cin.clear().
Since you don't have any code to check the state of cin and clear the state when appropriate, nothing is read in the loop after the first iteration.
One way to fix the problem is add a line to ignore the contents of the stream after the year is read.
cin >> user[i].year;
cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
Also, it is a good practice to always check the state of a stream after an IO operation to make sure that you don't overlook any errors.
for(int i = 0; i < N; i++)
{
cout << "Enter title: ";
cin.get(user[i].title, 60);
if ( !cin )
{
cout << "Error reading title.\n";
exit(1);
}
cout << "Enter year: ";
cin >> user[i].year;
if ( !cin )
{
cout << "Error reading name.\n";
exit(1);
}
// Ignore everything up to and including the newline character.
cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
So recently, I came across using isdigit as a way to check to see if an entered value for an int is actually an integer, rather than a string or a char.
However, when I wrote a short program to play around with that, the program failed to execute from that point on.
EDIT: I also in the program wanted to take the invalid data and convert it to a different data type.
Here is the code:
#include <iostream>
#include <sstream>
#include <string>
#include <cctype>
using namespace std;
int main()
{
int enterCFN;
char revisit;
int review(0);
cout << "Enter a digit: ";
cin >> enterCFN;
bool y = isdigit(enterCFN);
if (y == false)
{
// This is the data conversion section
revisit = enterCFN;
revisit = review;
cout << review << "\n";
}
else
{
cout << enterCFN << "\n";
}
return 0;
}
Is there anyone who can correct my error and show me what I'm doing wrong?
enterCFN is an int. It stores a number. isdigit() checks if a character represents a number. These are not the same thing: for example 32 is a number but char(32) means ' ' (space).
What you want instead is this:
if (cin >> enterCFN)
That will take the input from the user and check if it is valid all at once. No need for isdigit().
isdigit() checks if a given character is one of 0-9
For validating integer do something like following:
std::cout << "Enter a digit: ";
std::cin >> enterCFN ;
while (1)
{ if ( std::cin >> enterCFN )
{
// good input
break ;
}
else
{
std::cout << "Enter a digit: ";
// clear stream flags set due to bad input
std::cin.clear();
// get rid of the bad input.
// ignore the rest of the line
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
}
getRegionTotal() is the function I'm using for validation right now. It works pretty well in that if the user enters something like "twenty" or -7, it will not accept that and it will keep asking for new values until it gets one that is valid. However if the user enters 60.7 for the number of accidents in the north region, it will accept 60 and drop the .7 part. Then it will give both the regular instructions and the more specific instructions when it asks for the number of accidents in the south region.
//These will hold the number of accidents in each region last year
int northTotal = 0;
int southTotal = 0;
int eastTotal = 0;
int westTotal = 0;
int centralTotal = 0;
//passing 0 for northTotal, southTotal etc. because main doesn't know
//values of them until the function returns a value. When it returns a value
//it will go into the variables on the left. getRegionTotal will get the number
//of accidents for a region from the user and prompt the user using the string that
//is in the first argument.
northTotal = getRegionTotal("North", northTotal);
southTotal = getRegionTotal("South", southTotal);
eastTotal = getRegionTotal("East", eastTotal);
westTotal = getRegionTotal("West", westTotal);
centralTotal = getRegionTotal("Central", centralTotal);
int getRegionTotal(string regionName, int regionTotal)
{
//instructs user to enter number of accidents reported in a particular region
cout << "\nNumber of automobile accidents reported in " << regionName << " " << cityName << ": ";
//while regionTotal is not an integer or regionTotal is negative
while (!(cin >> regionTotal) || (regionTotal < 0) )
{
//give user more specific instructions
cout << "\nPlease enter a positive whole number for the number of\n";
cout << "automobile accidents in " << regionName << " " << cityName << ": ";
cin.clear(); //clear out cin object
cin.ignore(100, '\n'); //ignore whatever is in the cin object
//up to 100 characters or until
// a new line character
}
//returns a valid value for the number of accidents for the region
return regionTotal;
}
Parse the whole line and make sure you've consumed the whole line.
With iostreams:
#include <iostream>
#include <sstream>
#include <string>
for (std::string line; std::getline(std::cin, line); )
{
std::istringstream iss(line);
int result;
if (!(iss >> result >> std::ws && iss.get() == EOF))
{
// error, die. For example:
std::cout << "Unparsable input: '" << line << "'\n";
continue;
}
// else use "result"
}
With stdlib:
#include <errno>
#include <cstdlib>
char const * input = line.c_str(); // from above, say
char * e;
errno = 0;
long int result = std::strtol(input, &e, 10);
if (e == input || *e != '\0' || errno != 0)
{
// error
}
The two approaches are fundamentally identical, but the former may be more "idiomatic C++". That said, if you already have an existing string, the strtol-approach is a neat alternative, since it gives you precise error handling: did you consume the whole string (if not, e points to the next character); did you consume any of the string (if not, e points to the beginning); was there an overflow or underflow (check errno). On the other hand, the iostreams approach lets you consume trailing whitespace (thanks to >> std::ws), which the strtol-solution doesn't.
There's also std::stol which wraps strtol (and similarly for strtoull/strtod etc.), but it throws an exception on error, and I believe that exceptions are not the right tool for structuring control flow of normal behaviour like reading user input. Also, you cannot control how those wrappers operates; for example, the succeed even if they don't consume the entire string (but don't tell you how far they got), and you cannot specify the number base.