Weird behavior when using cin.ignore and cin.clear CPP - c++

I learned that cin.clear() clears the error flag, so the cin operations will work well, and cin.ignore() to extract characters from the stream.
So I tried this code:
#include <iostream>
#include <string>
int main()
{
std::string s;
std::cin >> s;
std::cout << s;
std::cin.clear();
std::cin.ignore(1000, '\n');
std::getline(std::cin, s);
std::getline(std::cin, s);
std::cout << s;
system("pause");
return 0;
}
It works well.
For three inputs:
I
AM
TRY
The output will be:
I
TRY
But if I change it to be:
#include <iostream>
#include <string>
int main()
{
std::string s;
std::cin >> s;
std::cout << s;
std::cin.clear();
std::cin.ignore(1000, '\n');
std::getline(std::cin, s);
std::cin.clear(); // New code
std::cin.ignore(1000, '\n'); // New code
std::getline(std::cin, s);
std::cout << s;
system("pause");
return 0;
}
I will need to enter four inputs!
How does it make any sense that when I add the code above, I will need to enter:
I
AM
NOW
TRY
To get the same output? For some reason it requires one input more.

Consider you intput I AM TRY NOW every time.
#include <iostream>
#include <string>
int main()
{
std::string s;
std::cin >> s;
std::cout << s; //-> outputs "I"
std::cin.clear();
std::cin.ignore(1000, '\n');//consumes all that follows "I"
std::getline(std::cin, s); //-> get the whole "I AM TRY NOW" inside s
std::cin.clear();
std::cin.ignore(1000, '\n'); //Your cin is empty (because you took the whole line with getline(), not just part of the line, the stream has no character left in it and this cin.ignore() call is the reason you need 1 more input, because calling cin.ignore() en empty stream does that.
std::getline(std::cin, s); //-> overwrites the previous std::getline(std::cin, s);
std::cout << s; //outputs the whole line : "I AM TRY NOW"
system("pause");
return 0;
}
Because you call cin.ignore(1000, '\n'); on an empty stream, you get one more input with the second code sample.
Try this
int main()
{
std::string s;
std::cin.ignore(1000, '\n'); // New code
system("pause");
}
It will require an input whereas this :
int main()
{
std::string s;
cin >> s;
std::cin.ignore(1000, '\n'); // New code
system("pause");
}
Will also require a single input if you enter I the newline will be the discarded character, if you enter I AM TRY then AM TRY and the newline will be discarded
int main()
{
std::string s;
cin >> s;
std::cin.ignore(1000, '\n'); // New code
std::cin.ignore(1000, '\n'); // requires second input
system("pause");
}
Will require two inputs because at the second cin.ignore call, there is an empty cin stram.

Related

Error with input-validation using std::cin

I have implemented this check in my program to determine if the input is of the right type or not, and if not it asks to re-write the input.
If the input is wrong it works just fine but, if the input is right, you need to write it again. How can I avoid this? (you can find an example here https://godbolt.org/z/KjoTbc)
#include <iostream>
#include <limits>
int main() {
int input;
std::cin >> input ;
while (!(std::cin >> input)) {
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
std::cout << "Please, write an integer number\n";
};
}
You are not checking the first std::cin >> input ; and running while (!(std::cin >> input)), which asks input, unconditionally.
Remove the first unchecked reading and try this:
#include <iostream>
#include <limits>
int main() {
int input;
while (!(std::cin >> input)) {
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
std::cout << "Please, write an integer number\n";
}
}

How to get string input n times? [duplicate]

This question already has answers here:
Why does std::getline() skip input after a formatted extraction?
(5 answers)
Closed 3 years ago.
I have a C++ program. I want to get a number from the user (t) and force the user to enter line t times but the program's execution terminates after 1 iteration. This is the code:
#include <iostream>
#include <string>
using namespace std;
int main()
{
string str;
int t;
cin >> t;
for (int i=0; i< t; i++) {
getline(cin, str);
cout << str;
}
return 0;
}
Can anyone explain me why this happening and how to solve it?
Thank you my friends.
The newline character is still in the buffer when you do cin >> t so the next line you read will be blank. When you mix formatted input (>>) and unformatted (std::getline) you often get in situations like this and you need to take measures when switching to unformatted input. Example remedy:
#include <iostream>
#include <limits>
#include <string>
using namespace std;
int main() {
string str;
int t;
cin >> t;
cin.ignore(numeric_limits<streamsize>::max(), '\n'); // skip the rest of the line
for(int i = 0; i < t; i++) {
if(getline(cin, str)) // check that the getline actually succeeded
cout << str << '\n';
else
break;
}
return 0;
}
When you enter your first character (the times to repeat), a character is left in the cin buffer - newlines are not consumed by cin >>. As a result, getline(cin, str) reads this character and takes it as the first input, which then empties the buffer out and lets you enter the others.
You can clear the buffer with std::cin.ignore(1); to remove that trailing character - this lets your code run as anticipated. Why not just use cin >> str, though? That solves the problem and avoids a call to getline.
#include <iostream>
#include <string>
using namespace std;
int main()
{
string str;
int t;
cin >> t;
//clear one character out of buffer
cin.ignore(1);
//note that 1 is used for demonstration purposes
//in development code, INT_MAX, numeric_limits<streamsize>::max(),
//or some other large number would be best, followed
//by std::cin.clear()
for (int i=0; i< t; i++) {
cout << "input: ";
//you could use cin >> str; instead of getline(cin, str);
getline(cin, str);
cout << "got: " << str << std::endl;
}
return 0;
}
Demo

Reading from a txt file with a specific format into variables in c++ [duplicate]

This question already has answers here:
Why does std::getline() skip input after a formatted extraction?
(5 answers)
Closed 3 years ago.
Is there a reason why if in my program I am asking the user for input, and I do:
int number;
string str;
int accountNumber;
cout << "Enter number:";
cin >> number;
cout << "Enter name:";
getline(cin, str);
cout << "Enter account number:";
cin >> accountNumber;
Why after inputting the first number, it outputs "Enter Name", followed immediately by "Enter Account Number" before I even get to input my "str" for the getline(cin, str) line? Thanks!
The getline(cin, str); reads the newline that comes after the number read previously, and immediately returns with this "line". To avoid this you can skip whitespace with std::ws before reading the name:
cout << "Enter number:";
cin >> number;
cout << "Enter name:";
ws(cin);
getline(cin, str);
...
Note that this also skips leading whitespace after the newline, so str will not start with spaces, even if the user did input them. But in this case that's probably a feature, not a bug...
Try
cout << "Enter name:";
cin.ignore();
getline(cin, str);
cin >> number
only grabs the numbers from the buffer, it leaves the "enter" in the buffer, which is then immediately grabbed up by the getline and interpreted as an empty string (or string with just the new line, i forget).
It looks like you want line based reading. For this you probably want to use getline consistently and then parse each line if you need to parse a number from then read line. It makes the input reading more consistent.
This way you don't have to manually scan for the end of each line to guarantee that the next read operation starts on a fresh line.
It also makes adding error handling for repeating input requests simpler.
e.g.
#include <string>
#include <iostream>
#include <istream>
#include <ostream>
#include <sstream>
int parse_integer(const std::string& input)
{
std::istringstream iss(input);
int result;
if (!(iss >> result))
{
// error - throw something?
}
return result;
}
int main()
{
int number;
std::string str;
int accountNumber;
std::string inputline;
std::cout << "Enter number: ";
if (!std::getline(std::cin, inputline))
{
// error - throw something?
}
number = parse_integer(inputline);
std::cout << "Enter name:";
if (!std::getline(std::cin, inputline))
{
// error - throw something?
}
str = inputline;
std::cout << "Enter account number:";
if (!std::getline(std::cin, inputline))
{
// error - throw something?
}
accountNumber = parse_integer(inputline);
return 0;
}
cin >> number // eat the numeric characters
getline(cin, str) // eat the remaining newline
I think the problem is that cin >> passes on the newline character (\n). The getline() assumes the newline character is whitespace and passes it on. Someone posted a solution you can use.
You can use a dummy getline(cin, dummy); or the real thing cin.ignore(100,'\n');
Don't use getline(): it's a bad thing for memory allocation. Use fgets(). See fgets reference.

String length getting reduced by using getline?

I am using getline and ignore but something is not working properly,
Below is the sample code which am not able to understand how it is working.
int main()
{
string str;
int t,length;
cin>>t; // t is the number of test cases
while(t--!=0)
{
cin.ignore();
getline(cin,str);
length=str.size();
cout<<"length="<<length;
}
}
Sample output:
2
hey hi
length 6
hey hi
length 5
Why is the length decreasing? Is this because of getline and ignore function? Any help would be appreciated.
The reason it is giving a different length is becaus your ignore() function ignores only one character. The first time round it ignores the return key you pressed after entering the number. But std::getline() deletes the return character for you. So the second time round ignore() deletes the first letter of your string making it "eh hi".
int main()
{
string str;
int t, length;
cin >> t; // does not remove the RETURN character
while(t-- != 0)
{
// first removed RETURN character after removes first letter
cin.ignore();
getline(cin, str);
length = str.size();
cout << "length = " << length;
}
}
Try using this instead:
int main()
{
string str;
int t, length;
cin >> t; // does not remove the RETURN character
while(t-- != 0)
{
// cin.ignore(); // dont do this here please
// cin >> ws skips all whitespace characters
// including the return character
getline(cin >> ws, str);
length = str.size();
cout << " length = " << length;
}
}
Alternatively (maybe better) you can move the ignore() function out of the loop to where t is really needed:
#include <limits>
int main()
{
string str;
int t, length;
cin >> t; // does not remove the RETURN character
// ignore as many characters as necessary including the return
cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
while(t-- != 0)
{
// cin.ignore(); // not here
getline(cin, str);
length = str.size();
cout << " length = " << length;
}
}
The cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); looks complicated but it is the only way to guarantee that any spurious characters (like spaces) are removed. You can probably get away with just cin.ignore() for the exercise if you want.
Read up on std::istream::ignore()
cin.ignore() defaults to ignoring one character.
If you output your string each time, you'll see that in later cases the string is equal to "ey hi". The h is being dropped.
The value of the string held by cin drops its first character before being passed to getline.
Since you're using getline, you can simply remove the cin.ignore() from your loop and your program should work as intended.
However, you should also change your cin>>t; line. In this case, the ignore() is dropping the line return after the input value 2. A stringstream here allows for a getline(...) function, or alternatively you can use cin.ignore(str.max_size(), '\n');.
In the case of the stringstream, your code would become:
#include <sstream> // stringstream
#include <string> // std::string
#include <iostream> // cin
int main()
{
string str;
int t,length;
getline(cin, str);
std::stringstream stream;
stream << str;
if (!(stream >> t)) {
// Couldn't process to int
}
// cin>>t; // t is the number of test cases
// No longer need this line.
while(t--!=0)
{
// cin.ignore(); Drop this too
getline(cin,str);
length=str.size();
cout<<"length="<<length;
}
}
If you are not interested in whitespace,
then use getline(cin >> ws, str)

Need help with getline() [duplicate]

This question already has answers here:
Why does std::getline() skip input after a formatted extraction?
(5 answers)
Closed 3 years ago.
Is there a reason why if in my program I am asking the user for input, and I do:
int number;
string str;
int accountNumber;
cout << "Enter number:";
cin >> number;
cout << "Enter name:";
getline(cin, str);
cout << "Enter account number:";
cin >> accountNumber;
Why after inputting the first number, it outputs "Enter Name", followed immediately by "Enter Account Number" before I even get to input my "str" for the getline(cin, str) line? Thanks!
The getline(cin, str); reads the newline that comes after the number read previously, and immediately returns with this "line". To avoid this you can skip whitespace with std::ws before reading the name:
cout << "Enter number:";
cin >> number;
cout << "Enter name:";
ws(cin);
getline(cin, str);
...
Note that this also skips leading whitespace after the newline, so str will not start with spaces, even if the user did input them. But in this case that's probably a feature, not a bug...
Try
cout << "Enter name:";
cin.ignore();
getline(cin, str);
cin >> number
only grabs the numbers from the buffer, it leaves the "enter" in the buffer, which is then immediately grabbed up by the getline and interpreted as an empty string (or string with just the new line, i forget).
It looks like you want line based reading. For this you probably want to use getline consistently and then parse each line if you need to parse a number from then read line. It makes the input reading more consistent.
This way you don't have to manually scan for the end of each line to guarantee that the next read operation starts on a fresh line.
It also makes adding error handling for repeating input requests simpler.
e.g.
#include <string>
#include <iostream>
#include <istream>
#include <ostream>
#include <sstream>
int parse_integer(const std::string& input)
{
std::istringstream iss(input);
int result;
if (!(iss >> result))
{
// error - throw something?
}
return result;
}
int main()
{
int number;
std::string str;
int accountNumber;
std::string inputline;
std::cout << "Enter number: ";
if (!std::getline(std::cin, inputline))
{
// error - throw something?
}
number = parse_integer(inputline);
std::cout << "Enter name:";
if (!std::getline(std::cin, inputline))
{
// error - throw something?
}
str = inputline;
std::cout << "Enter account number:";
if (!std::getline(std::cin, inputline))
{
// error - throw something?
}
accountNumber = parse_integer(inputline);
return 0;
}
cin >> number // eat the numeric characters
getline(cin, str) // eat the remaining newline
I think the problem is that cin >> passes on the newline character (\n). The getline() assumes the newline character is whitespace and passes it on. Someone posted a solution you can use.
You can use a dummy getline(cin, dummy); or the real thing cin.ignore(100,'\n');
Don't use getline(): it's a bad thing for memory allocation. Use fgets(). See fgets reference.