I am trying to access different words in a string using std::istringstream and I am also doing so with multiple test cases.
int t;
cin>>t;
while(t--)
{
string arr;
cin.ignore();
getline(cin,arr);
istringstream iss(arr);
string word;
while(iss>>word)
{
cout<<word;
}
}
For the first test case, everything is perfect (i.e. it outputs the correct words). But for every subsequent test case, the first letter of the first word is left out.
Example:
Input:
4
hey there hey
hi hi hi
my name is xyz
girl eats banana
And I'm getting:
Output:
hey there hey
i hi hi
y name is xyz
irl eats banana
Can anyone please suggest me what to do and why is this error occurring?
Your problem is that formatted input, i.e., something like in >> value conventionally skips leading whitespace before attempting to read. Unformatted input, on the other hand, doesn't skip leading whitespace. With the std::cin.ignore(); in your loop you make the assumption that std::getline(std::cin, arr) would leave the newline in the input like the input of t does. That is not so. std::getline() extracts and stores all characters up to the first newline where it stop, still extracting the newline. So, you'd remove the cin.ignore(); from the loop.
The key question becomes how to switch between formatted input and unformatted input. Since the newline upon entry of a numeric value may be preceded with arbitrary spaces which you probably also want to ignore, there are essentially to ways:
std::cin >> std::ws; skips all leading whitespace. That may include multiple newlines and spaces at the beginning of the line. Skipping those may not necessarily desirable.
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); ignores all characters up to and including the first newline. That would allow for empty lines to follow up as well as lines starting with leading whitespace.
This line is the culprit: cin.ignore();.
When std::basic_istream::ignore is called without any arguments, it ignores exactly 1 character.
In your case, std::cin.ignore() will ignore the first letter, but not for the first test case, because at that point std::cin is empty, so there is nothing to ignore. But then, std::cin has the other words in it, so it ignores 1 character from the first word.
According to the documentation of std::basic_istream::ignore:
Extracts and discards characters from the input stream until and
including delim. ignore behaves as an UnformattedInputFunction
Its worth to mention that std::basic_istream::ignore will block and wait for user input if there is nothing to ignore in the stream.
With this in mind, lets break down what your code does:
the first time you call this function in your loop, it is going to
ignore the new line character that is still in the buffer from the
previous cin>>t operation. Then the getline statment will wait and read a line from the user.
The next time around, since there is nothing in the buffer to
ignore(as std::getline doesn't leave the new line character in the
buffer), it is going to block and wait for input to ignore. So
the next time the program block and waits for input, it is because
of the ignore() function,not the getline function as you would
have hoped, and the moment you provide an input(i.e you second test
case),one character from the input is going to be ignored.
The next getline function will not block since there is something
in the buffer left by the previous ignore function after it
ignores the first character of the input so getline will read the
remaining string which will happen to be one character short.
The process continues from step 2 until your loop terminates.
int t;
cin>>t;//this leaves new line character in the buffer
while(t--)
{
string arr;
cin.ignore();//this will ignore one character from the buffer,so the first time
//it will ignore the new line character from the previous cin input
//but next time it will block and wait for input to ignore
getline(cin,arr);//this will not block if there is something in the buffer
//to read
...
}
The solution would be to move the ignore statement out of the loop and next to your cin>>t statement. It's also better write ignore(INT_MAX,'\n'); in this case. You might also want to read this answer to see when and how to use ignore.
Related
cin >> name;
cin >> age;
cout << name << age;
What exactly is happening here if I type a string, then some whitespace and a number? For example Something 20. Does it read Something then sees the whitespace and goes okay that's the end of this first line because a whitespace terminates the reading of the string, goes to the next input and reads 20?
But I also wanna be a bit more specific. Is it okay to say at first when I'm in the console typing Something, that's going into the standard input stream, then getting stored in the buffer and when I press that space it's like pressing enter? And that Something gets extracted and assigned to name? Then that 20 I type is like a whole new unrelated line because I pressed space earlier and so that gets extracted and assigned to age?
How they'll get extracted
The integer gets extracted via std::basic_istream::operator::>>:
Extracts values from an input stream
1-4 ) Extracts an integer value potentially skipping preceding
whitespace. The value is stored to a given reference value.
This function behaves as a FormattedInputFunction. After constructing and
checking the sentry object, which may skip leading whitespace,
extracts an integer value by calling std::num_get::get().
The string gets extracted via std::basic_string::operator>>:
2 ) Behaves as a FormattedInputFunction. After constructing and
checking the sentry object, which may skip leading whitespace, first
clears str with str.erase(), then reads characters from is and appends
them to str as if by str.append(1, c), until one of the following
conditions becomes true:
N characters are read, where N is is.width() if is.width() > 0,
otherwise N is str.max_size()
the end-of-file condition occurs in the stream is
std::isspace(c,is.getloc()) is true for the next character c
in is (this whitespace character remains in the input stream).
And in FormattedInputFunction:
if ios_base::skipws flag is set on this input stream, extracts and
discards characters from the input stream until one of the following
becomes true:
the next available character on the input stream is not
a whitespace character, as tested by the std::ctype facet of the
locale currently imbued in this input stream. The non-whitespace
character is not extracted.
the end of the stream is reached, in which
case failbit and eofbit are set and if the stream is on for exceptions
on one of these bits, ios_base::failure is thrown.
And as stated in Basic Input/Output from cplusplus.com:
...Note that the characters introduced using the keyboard are only transmitted to the
program when the ENTER (or RETURN) key is pressed.
...
...cin extraction always considers spaces (whitespaces, tabs,
new-line...) as terminating the value being extracted, and thus
extracting a string means to always extract a single word, not a
phrase or an entire sentence.
Testing
Compiling and testing your program with leading and trailing whitespaces via MSVC-v142 compiler:
AA 123 some trailing whitespaces
Prints out:
AA123
Read also
Stackoverflow: Clarify the difference between input/output stream and input/output buffer
Learn cpp: Input and output streams
As the title says - when should I use a blank cin.get() ?
I encountered situations when the program acted strange until I added a few blank cin.get()s between reading lines. (e.g. in a struct when reading its fields I had to enter a cin.get() between each non-blank cin.get())
So what does a blank cin.get() do and when should I use it?
Thanks.
There are two broad categories of stream input operations: formatted and unformatted. Formatted operations expect input in a particular form; they start out by skipping whitespace, then looking for text that matches what they expect. They typically are written as extractors; that's the >> that you see so often:
int i;
std::cin >> i; // formatted input operation
Here, the extractor is looking for digits, and will translate the digits that it sees into an integer value. When it sees something that isn't a digit it stops looking.
Unformatted input operations just do something, without regard to any rules about what the input should look like. basic_istream::get() is one of those: it simply reads a character or a sequence of characters. If you ask it to read a sequence it doesn't care what's in that sequence, except that the form that takes a delimiter looks for that delimiter. Other than that, it just copies text.
When you mix formatted and unformatted operations they fight with each other.
int i;
std::cin >> i;
If std::cin is reading from the console (that is, you haven't redirected it at the command line), you'll typically type in some digits followed by the "Enter" key. The extractor reads the digits, and when it hits the newline character (that's what the "Enter" key looks like on input) it stops reading, and leaves the newline character alone. That's fine, if the next operation on that stream is also a formatted extractor: it skips the newline character and any other whitespace until it hits something that isn't whitespace, and then it starts translating the text into the appropriate value.
There's a problem, though, if you use a formatted operation followed by an unformatted operation. This is a common problem when folks mix extractors (>>) with getline(): the extractor reads up to the newline, and the call to getline() reads the newline character, says "Hey, I've got an empty line", and returns an empty string.
Same thing for the version of basic_istream::get() that reads a sequence of characters: when it hits the delimiter (newline if you haven't specified something else) it stops reading. If that newline was a leftover from an immediately preceding formatted extractor, it's probably not what you're looking for.
One (really really ugly) solution is the brute force cin.ignore(256, '\n');, which ignores up to 256 sequential newline characters.
A more delicate solution is to not create the problem in the first place. If you need to read lines, read lines. If you need to read lines and sometimes extract values from the text in a line, read the line, then create a std::stringstream object and extract from that.
So I'm trying to go through input line by line. Each line in the input is formatted like this:
Words_More words_even more words_What I Need to Go Through_ Random_ Random_Etc.
With a random amount of word clusters (The words separated by '_')
I want, for each line, to be able to ignore all the words until I get to the fourth word cluster which in the example I gave would be: "What I Need To Go Through" and then store those separate words in some data structure that I haven't decided upon yet.
My first thought would be to use
getline(cin, trash, '_');
three times and deal with the data that follows, but then how would I loop line by line until the end of the input?
You basically have two options:
use getline for each line, then parse it
use getline(stream, string) to get a line from your stream, and store it into a string. Then construct an istringstream to parse this again (with the getline you thought of.
get what you need, and then ignore() stuff unill the next newline
You do getline() thing, and then you call ignore() (doc)
to read and discard the rest of the line, so you can start again with the next line.
which one you use is up to you. but the second one has slightly better performance, if you care about that stuff.
I wanted to clear some doubt regarding the function
cin.ignore(1,'\n');
code:
char x[80];
cin>>x;
cin.ignore(1,'\n');
If user input the word: paul Smith
does the program looks for the first space in the word and ignores/delete the rest of the characters?
Hence the program takes paul only and discards Smith?
Am I right?
I'm getting confused! Please explain in really simple words because I cannot understand the explanation on google regarding this issue.
cin.ignore(1,'\n');
is not very useful. It will ignore only one character.
cin.ignore(100,'\n');
will ignore up to 100 characters but will stop after it encounters a '\n'.
In your case,
cin>>x;
will read paul into x. The line
cin.ignore(1,'\n');
will consume the space after paul. Hence, Smith will be left on the input stream.
Hence the program takes paul only and discards Smith?
No. I hope that is clear from the above.
cin >> x;
Since x is a string or char array, this reads one word (everything up to the first whitespace character) from the input, and stores it in x.
cin.ignore(1, '\n');
reads and ignores one character from the input. It won't read the whole rest of the line. More generally:
cin.ignore(n, delim);
reads and ignores characters until it has either read n characters or reached a character equal to delim. If you want to ignore until the end of the line, no matter how many characters that is, do:
cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
I would like to figure out how to read a whole line (including spaces) with std::cin. I am aware of the existence of std::getline, I would just like to figure out how to do it with std::cin so I can better understand iostream in C++. I've tried using a for loop with std::cin, however it keeps reading past the end of the line. Any help would be greatly appreciated.
Also the cin << only allows us to enter one word into a string.
However, there is a cin function that reads text containing blanks.
std::cin.get(name, MAX);
get will read all characters including spaces until Max characters have been read or the end of line character (ā\nā) is reached and will put them into the name variable.
You should decide what is MAX.