I am trying to create a program which obtains names and test scores and places them into separate vectors. I am using getline() so I can get both first and last names. However, whenever I use the below code every name after the first loses its first letter. So, if the input for names was John Smith, Jane Smith, Jacob Smith, etc. The program outputs John Smith, ane Smith, acob Smith,... I tried taking out the cin.ignore()'s but then the program runs through after the first entry. I also tried creating separate functions for obtaining the names and scores, but this did not work either. Any advice or help would be appreciated.
int main() {
string test_taker;
int num_of_students = 0;
cout << "How many students took the exam? ";
cin >> num_of_students;
int starting = 0;
vector <double> score_list_us(num_of_students);
vector <string> name_list_us(num_of_students);
vector<double> score_list_sorted(num_of_students);
vector<string> name_list_sorted(num_of_students);
for (int i =0; i < num_of_students; i++) {
cout << "Student #" << i + 1 << " Name: ";
cin.ignore();
getline(cin, name_list_us[i]);
cout << "Student #" << i + 1 << " Score: ";
cin >> score_list_us[i];
cin.ignore();
}...
Problem: Missing character
See that cin.ignore(); right in front of getline(cin, name_list_us[i]);? Guess what it does.
On the first pass through the loop that cin.ignore(); eats the newline left by cin >> num_of_students; Every other time through the loop there is no newline to be eaten, so it eats part of your data.
Solution
Always put ignore()s, if you can't avoid them entirely, AFTER the operation that left the garbage you want gone. If you put ignore() before another operation, sooner or later you're going to hit that operation without garbage in the stream. In this case you hit it sooner.
Next, you want to ignore() everything up to and including the newline. Anything the user typed in after the input we want is garbage and should be discarded. Otherwise ignore() takes out a space character or something equally useless and leaves the newline for getline to trip over. I'll leave it to an old friend to explain what you need to do.
Specifically, use
cin.ignore(numeric_limits<streamsize>::max(), '\n');
So
int main() {
string test_taker;
int num_of_students = 0;
cout << "How many students took the exam? ";
cin >> num_of_students;
// ignore after
cin.ignore(numeric_limits<streamsize>::max(), '\n');
int starting = 0;
vector <double> score_list_us(num_of_students);
vector <string> name_list_us(num_of_students);
vector<double> score_list_sorted(num_of_students);
vector<string> name_list_sorted(num_of_students);
for (int i =0; i < num_of_students; i++) {
cout << "Student #" << i + 1 << " Name: ";
// not before
getline(cin, name_list_us[i]);
cout << "Student #" << i + 1 << " Score: ";
cin >> score_list_us[i];
// because this guy eats the newlines
cin.ignore(numeric_limits<streamsize>::max(), '\n');
}
Related
This question already has answers here:
Why does std::getline() skip input after a formatted extraction?
(5 answers)
Using getline() in C++
(7 answers)
Closed 4 years ago.
Can anyone explain why I am having trouble with the below C++ code?
#include <iostream>
using namespace std;
class stud
{
public:
string name,adrs;
long long unsigned int mob;
};
int main()
{
stud s[10];
unsigned int num;
cout << endl << "Enter the number of students(<10): ";
cin >> num;
cout << endl;
for(int i = 0; i < num; i++)
{
cout << endl << "Enter student " << i+1 << " name(put '.' at end and press enter): ";
getline(cin, s[i].name); // this line skips some data before even they are
//entered and there is no error while compiling
}
system("CLS");
for(int i = 0; i < num; i++)
{
cout << endl << " Student " << i+1 << " name is: ";
cout << s[i].name << endl;
}
return 0;
}
When I try to input a string value for an object in the array as above, using getline() without any delimiter (which uses a new line by default), I don't get correct output since some other data is automatically being skipped.
But, when I use getline() as follows instead of above, it works fine, but it needs a delimiter at the end:
getline(cin, s[i].name, '.');
Please help me find a solution. I think the Enter key is pressed several times at one key press, and that's why the getline() skips some data. I'm not sure about this, though.
before correcting you program one thing to know is that
Actually, a newline is always appended to your input when you select Enter or Return when submitting from a terminal.
cin>> doesn't remove new lines from the buffer when the user presses Enter.
This has little to do with the input you provided yourself but rather with the default behaviour std::getline() exhibits. When you provided your input for the name (std::cin >> num;), you not only submitted the following characters, but also an implicit newline was appended to the stream, getline() mistakes this for user input along with enter.
It is recommended to use cin.ignore() to get rid of those extra characters after using cin>>(whatever) if you are going to use getline(cin,any string) later.
Edit this part of your code:
stud s[10];
unsigned int num;
cout << endl << "Enter the number of students(<10): ";
cin >> num;
cout << endl;
cin.ignore();//just add this line in your program after getting num value through cin
//fflush(stdin);
//cin.sync();
//getchar();
for(int i = 0; i < num; i++)
{
cout<<endl<< "Enter student " << i+1 << " name(put '.' at end and press enter): ";
getline(cin,s[i].name);
}
system("CLS");
you can use and may be tempted to use fflush(stdin) also but it is not recommended as it has undefined behaviour, as
According to the standard, fflush can only be used with output buffers, and obviously stdin isn't one.
about cin.sync():
using “cin.sync()” after the “cin” statement discards all that is left in buffer. Though “cin.sync()” does not work in all implementations (According to C++11 and above standards).
you can also use getchar() to get the newline caused by Enter
int main()
{
for (int i = 0; i < 4; ++i)
{
cout << "Please enter Employee #" << (i+1) << "'s" << " name: ";
cin.ignore();
getline(cin, employee[i].employeeName);
cout << "Please enter Employee #" << (i+1) << "'s hours worked: ";
cin >> employee[i].hoursWorked;
cout << "Please enter Employee #" << (i+1) << "'s hourly rate: ";
cin >> employee[i].hourlyRate;
cout << "Please enter Employee #" << (i+1) << "'s Federal Tax Rate: ";
cin >> employee[i].fedtaxRate;
cout << "Please enter Employee #" << (i+1) << "'s State Tax Rate: ";
cin >> employee[i].statetaxRate;
cout << endl;
}
As the title says, I currently have cin.ignore(); to help with taking in a full name like "Barkley, Charles" (it does a fine job, except it truncates the first character leaving us with "arkley, Charles" when printed) but when I remove cin.ignore();, like 99% of the StackOverflow questions that have been answered say, nothing happens. In fact, it even gets worse: on the second loop, it will take the second line of the second loop and put it on the first line of the second loop. It's just a mess. Oh, and cin.getline doesn't work either. I tried that in conjunction with cin.sync and no dice. Regular plain old vanilla cin >> causes the same second line on first line problem. I don't know what to do. I haven't found a SO article that covers this (seemingly) edge case. Thanks for the help.
As with many input questions, the problem exists because you're mixing line-based and item-based input.
What will happen is that the final cin >> employee[i].statetaxRate; will leave the stream pointer pointing to a position immediately after the last valid character of its type.
That's likely to be the newline at the end of the line so, when you then go back to get the next name (without the cin.ignore), it will read that as an empty line.
You may think you can fix it by placing the cin.ignore at the end of the loop but you can run into other problems with that. Specifically, since that form simply skips one character, entering:
123<space><newline>
will simply skip the <space> and you'll have exactly the same issue.
A quick fix is to simply read the entire rest of the line with something like (at the end of the loop):
{
std::string junk;
getline(cin, junk);
}
and this will prep the input stream so you're at the start of the next line.
The other possibility (and this is preferred since the first time you enter abc where it's expecting a number is going to cause you grief) is to read all your items as lines and then use string processing to put them into numeric variables, something like strtod().
I am completing a lab assignment where the user is prompted for the type if fish they wish to order and to enter the price per pound. The user needs to be prompted for the type of fish and the price two times before the report prints.
The problem is that the program ends before the first instance of the loop has completed. (The way the code is written the titles on the report will print twice, but that was in the instructions.)
The code is below and any assistance is greatly appreciated.
#include <iostream>
#include <iomanip>
#include <string>
using namespace std;
int main()
{
float price;
string fishType;
int counter = 0;
// Change the console's background color.
system ("color F0");
while (counter < 3){
// Collect input from the user.
cout << "Enter the type of seafood: ";
cin >> fishType; // <------ FAILS AT THIS POINT. I GET THE PROMPT AND AT THE "ENTER" IT DISPLAYS THE REPORT
cout << "Enter the price per pound using dollars and cents: ";
cin >> price;
counter++;
}
// Display the report.
cout << " SEAFOOD REPORT\n\n";
cout << "TYPE OF PRICE PER" << endl;
cout << "SEAFOOD POUND" << endl;
cout << "-------------------------------" << endl;
cout << fixed << setprecision(2) << showpoint<< left << setw(25)
<< fishType << "$" << setw(5) << right << price << endl;
cout << "\n\n";
system ("pause");
return 0;
}
The new line character will not have been consumed by the read, using std::istream::operator>>(float), of the price:
cin >> price; // this will not consume the new line character.
The presence of the new line character during the next read, using operator>>(std::istream, std::string)), into fishType:
cin >> fishType; // Reads a blank line, effectively.
and then the user input that was intended to be the next fishType will be read (and fail to be) by the price as it will not be a valid float value.
To correct, ignore() until the next new line character after the read of the price. Something like:
cin.ignore(1024, '\n');
// or: cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
Always check the status of input operation to determine if they were successful or not. This is easily achieved:
if (cin >> price)
{
// success.
}
If the fishType can contain spaces then using operator>>(std::istream, std::string) is not appropriate as it will stop reading at the first whitespace. Use std::getline() instead:
if (std::getline(cin, fishType))
{
}
When the user enters input a new line character will be written to stdin, i.e cin:
cod\n
1.9\n
salmon\n
2.7\n
On first iteration of the loop:
cin >> fishType; // fishType == "cod" as operator>> std::string
// will read until first whitespace.
and cin now contains:
\n
1.9\n
salmon\n
2.7\n
then:
cin >> price; // This skips leading whitespace and price = 1.9
and cin now contains:
\n
salmon\n
2.7\n
then:
cin >> fishType; // Reads upto the first whitespace
// i.e reads nothin and cin is unchanged.
cin >> price; // skips the whitespace and fails because
// "salmon" is not a valid float.
I'm just starting to learn c++ and in this whole pointer / arrays chapter I'm trying to code something to get a grip on those concepts. What I want my code to do is: 1) ask for a number of students, 2) ask for a name for each student, 3) assign an ID and name to this student in a struct, 4) print the student ID and names for verification.
The problem is the following.
Let's say for example I choose to type 3 students and type the following names:
JOHN
GEORGE
NICK
What the program prints back as student ID/Names will be:
0 JOHN
1 EORGE
2 ICK
It seems to cut the first letter of all names but the first one.
#include <iostream>
using namespace std;
struct STUDENT
{
char chName[256];
int nID;
};
int main(){
//array tests
int i=0;
int nLoops=0; //number entered by user
STUDENT *pnStudents; //pointer to our student array
cout << "Enter number of students: ";
cin >> nLoops;
pnStudents = new STUDENT[nLoops];
for (i=0 ; i < nLoops ; i++)
{
cout << endl << "Full Name of Student " << i <<": ";
cin.ignore();
cin.getline(pnStudents[i].chName,255);
pnStudents[i].nID=i;
}
for (i=0 ; i < nLoops ; i++)
{
cout << pnStudents[i].nID << " " << *pnStudents[i].chName << endl;
}
system("pause");
return 0;
}
for (i=0 ; i < nLoops ; i++)
{
cout << endl << "Full Name of Student " << i <<": ";
cin.ignore();
cin.getline(pnStudents[i].chName,255);
pnStudents[i].nID=i;
}
In the first iteration of the loop, ignore() extracts a new line character that has been left in the stream by cin >> nLoops; call. istream::getline(), on the other hand, discards the trailing '\n' and so each subsequent iteration waits for and discards the first character of your input.
Move cin.ignore() just before the loop.
cin.ignore(); without an argument discards from cin, a stream of size 1.
istream& ignore ( streamsize n = 1, int delim = EOF );
Extract and
discard characters Extracts characters from the input sequence and
discards them.
Remove that. That seems to be the only thing in your code, which could eat one character away. Also, if you need to use ignore, use something like:
cin.ignore(256,' ');
^ ^ (number of char, delim)
If you are trying to ignore the character you took as input for nLoops, you need not do it in every iteration.
This is my first attempt at C++, following an example to calculate a tip through a console application. The full (working code) is shown below:
// Week1.cpp : Defines the entry point for the console application.
#include "stdafx.h"
#include <iostream>
#include <string>
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
// Declare variables
double totalBill = 0.0;
double liquour = 0.0;
double tipPercentage = 0.0;
double totalNoLiquour = 0.0;
double tip = 0.0;
string hadLiquour;
// Capture inputs
cout << "Did you drink any booze? (Yes or No)\t";
getline(cin, hadLiquour, '\n');
if(hadLiquour == "Yes") {
cout << "Please enter you booze bill\t";
cin >> liquour;
}
cout << "Please enter your total bill\t";
cin >> totalBill;
cout << "Enter the tip percentage (in decimal form)\t";
cin >> tipPercentage;
// Process inputs
totalNoLiquour = totalBill - liquour;
tip = totalNoLiquour * tipPercentage;
// Output
cout << "Tip: " << (char)156 << tip << endl;
system("pause");
return 0;
}
This works fine. However, I want to move:
cout << "Please enter your total bill\t";
cin >> totalBill;
to be the first line under:
// Capture inputs
But when I do the application breaks (it compiles, but just ignores the if statement and then prints both cout's at once.
Im scratching my head becuase I cant understand what's going on - but I'm assuming I'm being an idiot!
Thanks
Try this
// Capture inputs
cout << "Please enter your total bill\t";
cin >> totalBill;
cin.clear();
cin.sync();
See c++ getline() isn't waiting for input from console when called multiple times
Or, better yet don't use getline at all:
cout << "Please enter your total bill\t";
cin >> totalBill;
cout << "Did you drink any booze? (Yes or No)\t";
cin >> hadLiquour;
totalBill is a number, i.e. the program "consumes" everything from your input that is a number. Let's say you entered:
42.2[RETURN]
The 42.2 gets copied into totalBill. The [RETURN] doesn't match, and remains in the input buffer.
Now, when you call getline(), the [RETURN] is still sitting there... I am sure you can figure out the rest from there.
Cin doesn't remove the newline character from the stream or do type-checking. So using cin>>var; and following it up with another cin >> stringtype; or getline(); will receive empty inputs. It's best practice to NOT MIX the different types of input methods from cin.
[for more informations see link]
you may change your code as below :
cout << "Please enter your total bill\t";
getline(cin, hadLiquour); // i used the hadLiquour string var as a temp var
// so don't be confused
stringstream myStream(hadLiquour);
myStream >> totalBill;