What exactly empty input means for cin.get()? - c++

I think it's a simple question, but I don't understand the concept in this sample of code, mainly in the while loop:
#include <iostream>
const int ArSize = 10;
void strcount(const char * str);
int main(){
using namespace std;
char input[ArSize];
char next;
cout << "Enter text:\n";
cin.get(input, ArSize);
while(cin){
cin.get(next);
while(next != '\n')
cin.get(next)
strcount(input);
cout << "Enter next line, empty line ends the program:\n";
cin.get(input, ArSize);
}
cout << "The end\n";
return 0;
}
...
What I understand is that the while loop continues until cin returns false. It filters out the remaining input that's left in the buffer (because it wasn't the size of ArSize or under, or it was - then it will just filter out the newline character) until it meets the newline character. Then it counts string's characters (not important in this question), and then, let's say someone just presses enter. cin.get() discards newline character in input. So if someone for example enters an empty line of text in the terminal, it reads it as 'failed' input and cin returns false? Because if someone proceeds to the new line, just by pressing enter, it just leaves the newline character in the buffer, and cin.get() can't get it so it returns false. Or am I wrong?
In short - What exactly happens if you just press enter? cin.get() can't get the input because there's only newline in buffer and it counts it as failed input, so it returns false?

If cin.get(input, ArSize); reads no characters (i.e. the first character it encounters is a newline) it calls setstate(failbit) putting the stream into a failed state and therefore while(cin) becomes false, ending the loop.

As you can see here from the CPP reference https://en.cppreference.com/w/cpp/io/basic_istream/get
cin. get() is used to read the next character from the keyboard buffer and it returns that character in case it was available to be read and returns EOF otherwise and sets failbit and eofbit (which makes the expression in the if statement evaluates to false).
now let's see the code in action line by line:
while(cin){
this evaluates to true as long as the failbit flag in the cin object is set to goodbit showing no error. (https://en.cppreference.com/w/cpp/io/ios_base/iostate)
cin.get(next);
while(next != '\n')
cin.get(next)
the first line reads the next character from the keyboard buffer and stores it in the next variable and the while loop checks for the newline character which is equivalent to pressing Enter if it is not the next character in the buffer then continue reading and storing in next until it meets a newline character then it exits the loop returning to the outer while loop.
strcount(input);
cout << "Enter next line, empty line ends the program:\n";
cin.get(input, ArSize);
then strcount function as I assume is used to count the characters entered by the user in the input array by this line of code before the while loop.
cin.get(input, ArSize);
and then at the last line inside of the while loop, the program reads another input by the user.
Please Note:
the use of these three lines here is to make sure that each line is read at every single loop with no characters read in the second input before the newline character appears even if the number of characters is bigger than the ArSize variable. when that happens the first line before the while loop will read the number of ArSize from the buffer and if there are remaining characters other than the newline it will be read by the three lines until a newline appears so that the next get function will start looking for characters in the buffer after the previous newline.
cin.get(next);
while(next != '\n')
cin.get(next)
if there is anything unclear please let me know.

Related

While loop with getline doesn't end for user input

I thought getline stops at a newline character, but the while loop does not end? it returns the correct data but it just sits in the terminal window. For example:
Enter an expression: #5+4#5+4
(blinking cursor)
(can enter data forever and press enter forever and it wont exit)
my code, (main.cpp):
int main()
{
string exp;
cout << "Enter an Infix Expression:";
while (getline(cin, exp, '#'))
{
string token = exp;
string post;
cout << token << endl;
IntoPost *infix = new IntoPost(token.length());
post = infix->inToPost(token);
cout << post << endl;
}
cin.get();
}
The Solution Using EOF
Your current program is looping endlessly because getline returns std::basic_istream, so while(getline()) will never equate to 'false'.
As #0x499602D2 has stated, your program is working as intended, but the extraction from getline can only end in two ways, as indicated by the reference here:
Extracts characters from is and stores them into str until the delimitation character delim is found (or the newline character, '\n', for when no delimiter is specified).
The extraction also stops if the end of file is reached in is or if some other error occurs during the input operation.
The first condition is difficult to pull off, as inputs on console are triggered by the \n character.
As for the second condition, as per #DavidC.Rankin:
You can also generate a manual EOF on Linux with [Ctrl+d] or windows with [Ctrl+z] (generally twice is required)
This means the solution is to use [Ctrl+d] or [Ctrl+z] to trigger the second condition to end your while loop at any time.
Alternative Using a Break Statement
One alternative way you can try to end the loop instead is breaking on input of an 'exit' string:
(1)
#include <algorithm>
//...
while (getline(cin, exp, '#'))
{
// removes meaningless endline chars from input
exp.erase(std::remove(exp.begin(), exp.end(), '\n'), exp.end());
if (exp == "exit"){
break;
}
//... Your While Block Code Here!
}
To break out of your while loop, you can simply use:
exit#
# Note, the endls from your couts in the loop will bleed into your inputs on your next while (getline(cin, exp, '#')), giving us unwanted newlines. To prevent this, we can get rid of the endlines from the inputs by using std::erase(). If you wish to keep those endlines in your input, simply set string token = exp; in front of the erase() line.
That's right, getline blocks the execution of the loop until a line separator is received and returns while that all is well, in the next step everything is repeated. If you want the loop not to be infinite - then put the Boolean variable key in the loop condition, and from the input check if the last character is an exit symbol and if so switch the variable key

Issue When Clearing cin Buffer After Using cin.getline()

I am using getline (the cin.getline() one) to get a string from cin and finding an issue with a special case. If a user inputs more characters than the the streamsize argument (in this case, 50), the cin buffer holds them and puts them into the next cin call. If I use cin.clear() and cin.ignore() and the user enters in fewer characters than the streamsize argument, then the program waits for the user to press enter again before it continues. So I use strlen to check the size of the string and only use cin.clear() and cin.ignore() if the string has 50 characters. This chops off the extra characters that the user entered after the 49th character. The problem is that when the user enters exactly 49 characters, then there are no extra characters in the buffer to chop off with the cin.clear() and cin.ignore() calls and therefore the program will sit and wait for the user to press enter another time.
A couple of questions:
1) Is there a flag I can check to see if there are characters in the buffer so I can clear() and ignore() only when this flag is true?
2) Is there any other way I can call this same getline function that cuts off all characters after the streamsize argument?
Here is my code:
#include <iostream>
#include <cstring>
using namespace std;
#define SIZE 50
void getString(char*);
int main() {
char words[SIZE];
getString(words);
return 0;
}
void getString(char* words) {
cout << "Enter your string: ";
cin.getline(words, SIZE);
if (strlen(words) == SIZE - 1) {
cin.clear();
cin.ignore(numeric_limits<streamsize>::max(), '\n');
}
}
A sample 49 character input that would cause the issue:
abcdefghijklmnopqrstuvwxysabcdefghijklmnopqrstuvw
Erase or add one letter to see the program's normal performance.
You can use istream::gcount() to decide whether there are any more characters other than '\n' left in the line still.
Here are the cases you need to think about.
The return value of cin.gcount() is less than SIZE-1. In this case, there is nothing left in the line. You don't have to worry about ignoring the rest of the line.
The return value of cin.gcount() is SIZE-1. This could be due to two scenarios.
The user enters SIZE-2 characters followed by a newline. In this case, there is nothing left in the line. You don't have to worry about ignoring the rest of the line.
The user enters SIZE or more number of characters followed by a newline. In this case, there are still some characters left in the line. You will want to ignore the rest of the line.
The return value of cin.gcount() is SIZE. This can happens only when the user enteres SIZE-1 characters followed by a newline. All the characters from the line are read into the argument provided to the function. The newline character is read and discarded. You don't have to worry about ignoring the rest of the line.
Give the above cases, the only time you have to worry about ignoring the rest of the line is when you run into case 2.2. That condition is met when cin.gcount() == SIZE-1 and strlen(words) == SIZE-1.
void getString(char* words) {
cout << "Enter your string: ";
cin.getline(words, SIZE);
if (cin.gcount() == SIZE-1 && strlen(words) == SIZE-1)
{
// There are characters in the stream before the \n.
cin.clear();
cin.ignore(numeric_limits<streamsize>::max(), '\n');
}
}

why does my program skip steps using getline()?

I think my program skips steps because I use getline() inside While and For loops without using cin.clear() and cin.ignore(). If I'm right, where will I have to insert them?
I tried to write the code with cin.clear() and cin.ignore(10000, '\n') after each getline() (really I don't know how these functions work properly, I found them surfing on Google) and the code doesn't work correctly.
The code is :
main.cpp
#include <iostream>
#include <list>
#include <string>
#include <vector>
#include <sstream>
#include <iostream>
#include <utility>
using namespace std;
int main(){
string S;
cout<<"insert test....input exaple: test 1"<<endl;
getline(cin, S);
while(S!="-1"){
cout<<"START WHILE"<<endl;
int nB = 0; //number of Lego Bricks;
cout<<"insert number of Lego bricks"<<endl;
cin>>nB;
for(int i=0; i<nB; i++){
cout<<"START FOR"<<endl;
cout<<"insert Lego brick (the number of faces must be even)....input example: NameBrick -> face1 face2 face3 face4...."<<endl;
getline(cin, S);
}
getline(cin, S);
}
return 0;
}
cout<<"insert number of Lego bricks"<<endl;
cin>>nB;
This is the problem. getLine() keeps reading until the next newline character. Let us say that '|' is our newline symbol.
When you write something into the terminal or when you are reading from file you have many lines to work through.
Example input(Greeting, name, age and hobby):
Hello.|
Magnus Elden|
24|
Tennis|
GetLine() will give you the entire line. The first call to getLine() will return the string "Hello." and stop since it reached a newline character, '|'.
The important part is that it stops AFTER the newline character.
Thus, the next time the first character it reads is the 'M' in "Magnus Elden".
When you get to the age part of the input, you use cin which only read the first item, be it a string or a number. As such it will stop BEFORE the newline character. The next time you call getLine() it reads until a newline character comes, but since the newline character still remains in the buffer getLine() returns immediately with "" as its return value.
That is why it seems to skip a step.
Step by step breakdown:
You input your greeting "Hello." and the buffer will then look like this:
Buffer:Hello.|
Let us say that the start of the reading is denoted by a ^. These are the steps.
Step 1.
Input:Hello.
Buffer:Hello.|
^
Function call:getLine()
Return value:"Hello."
Buffer:
Step 2.
Input:Magnus Elden
Buffer:Magnus Elden.|
^
Function call: getLine()
Return value:"Magnus Elden"
Buffer:
Step 3.
Input:24
Buffer:24|
^
Function call: cin
Return value:24
Buffer:|
Step 4.
Input:Tennis.
Buffer:|Tennis.|
^
Function call: getLine() //Remember that it runs until the first newline '|'.
Return value:""
Buffer:Tennis|
Just change the code bit to:
cout<<"insert number of Lego bricks"<<endl;
getLine(cin, s);
nB = atoi(s.c_str());
I hope this helps.
getline(cin,s) will do what you expect. The rest of the line (except the newline itself) is stored in s. And, crucially, the newline itself will be extracted and discarded.
So, if you call getline(cin,s) multiple times, each line will be read once, as you expect.
Similarly, assuming x in an int, cin >> x will read an integer. Multiple consecutive calls to cin >> x will read numbers from the input into x. Each time you call cin>>x it will skip over any whitespace before the number, then read the number. So, if you have a set of numbers, perhaps on the same line separated by spaces, or perhaps on different lines separated by newlines, then cin>>x will read them for you.
But it will not read-and-discard any whitespace after the number. Whitespace is discarded at the start of each call to cin>>x (i.e. before the number itself is read), but not after the number is read.
The problem occurs if you have a cin>>x followed by a getline. Imagine you type a number and then press enter. cin>>x will consume the number. but cin>>x will not consume the newline. Then, the getline will attempt to read the rest of the line. It will not try to read the next line. We're are still stuck on the same line as the number. You probably hit Enter immediately after entering the number, therefore the "rest of the line" is just an empty string.
There's a difference between "typing a number" and "typing a number followed by hitting the Enter key"
Thanks to all.
I solved replacing cin>> with getline how many of you suggested.
precisely:
cout<<"insert number of Lego bricks"<<endl;
cin>>nB;
with
cout<<"insert number of Lego bricks"<<endl;
getLine(cin, s);
nB = atoi(s.c_str());

Condition in while loop using cin

I have following code
//in main
int x;
while ( cin>>x ){
// code goes here
}
Now,i know that this loop executes untill read to x fails which occurs when type mismatch occurs
So pressing a char lets me to come out of loop as x is of type int and char will not be read from input stream.
But problem is with whitespaces, as they are also not int so why loop does not ends when i press enter or whitespace?
According to The C++ Programming Language Special Edition, $21.9 Advice [5]:
Remember that by default >> skips whitespace; §21.3.2.
And in $21.3.2:
Whitespace is defined as the standard
C whitespace (blank, tab, newline, formfeed, and carriage return) by a call to isspace() as defined
in (§20.4.2).
The comment of #jrok provided the answer.
Hence, as long as the input is a white space cin waits for input which may represent the requested type. It stops if an invalid character is in the stream or - after a valid non white space character has been consumed - the character is a white space.
You might read line by line to detect empty input:
std::string line
while(getline(cin, line)) {
// Note: Omitting the case where the line contains spaces, only.
if(line.empty()) {
// No input;
break;
}
else {
// Parse and process the line.
}
}
Note: If skipping white spaces is disabled, the behavior changes.

Using cin.get() to grab a line of text, then using it in a loop to display that line?

Ok, so I came across this code snippet in my textbook that's supposed to echo every other character a user types in. Now, I understand the every other character part, but I'm having difficulty with the use of cin.get(). I understand why the first cin.get() is there, but why is it also inside the loop? I'm guessing I'm not fully grasping the nature of input streams...
EDIT: It just clicked... I'm an idiot. Thanks for clearing that up.
char next;
int count = 0;
cout << "Enter a line of input:\n";
cin.get(next);
while (next != '\n')
{
if ((count%2) == 0)
cout << next;
count++;
cin.get(next);
}
Thanks in advance!
cin.get in this case does not "grab a line of text" as you seem to believe. cin.get in this case grabs just a single character. With cin.get you read characters the user is typing in, one by one, one after another. This is why you have cin.get in a loop.
The call to cin.get(next) that comes before the loop is only placing the first character of buffered user input into the variable 'next.'
Once inside the loop, and the character stored in 'next' has been processed (echoed if at an even index, otherwise ignored), cin.get(next) needs to be called again to retrieve the next character.
Its printing characters present at even positions
char next;
int count = 0;
cout << "Enter a line of input:\n";
cin.get(next);//gets first character (position zero) from input stream
while (next != '\n')//check whether the character is not line feed(End of the line)
{
if ((count%2) == 0)//checks if position is even
cout << next; //if yes print that letter
count++; //increments the count
cin.get(next); //gets next character from input stream
}
We require two cin.get(...)
before entering the while loop we need to know first character(position zero)
inside while loop for getting next character
but what is the use of outside cin.get(ch) what does it do
how cin.get() works inside loop
both behaviours are lookin different
so it's making confusing
there is a statement in loop to print the character got using cin.get(next) but it will not print it.... it will print all together after pressing enter key ... actually it should display the characters as we type from the keyboard but it is not actually working like that
istream& get(char &c) gets a character from the input stream.
so on the first call cin.get(next) you typed:
"hello world!"
Then future cin.get(next) will fetch h, e, l, l, etc... on every call until the there are no more characters in the input stream and that's when it will block asking the user for more input.
Streams in C++ , are buffered. Think of them as a line of letters. When you call cin.get(var) the first character in that line is removed and returned to you. So, that's how it works.
An example would help better. When the first cin.get() is executed, let's say you type in :
LISP
and then, cin.get() will return (in the var.) L and then the buffer will look like ISP... the next call will place I in the var. and the buffer will look like SP and so on...