3

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?

Rakete1111
  • 47,013
  • 16
  • 123
  • 162
amora
  • 135
  • 2
  • 8

3 Answers3

4

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:

  1. 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.
  2. 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.
Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
2

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.

Rakete1111
  • 47,013
  • 16
  • 123
  • 162
  • I think in the first case there is something to ignore,i.e the new line character, but the next time the user input will already have been skipped one character by the time it gets to the `getline` statement. – Biruk Abebe May 29 '16 at 19:32
2

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:

  1. 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.
  2. 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.
  3. 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.
  4. 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.

Community
  • 1
  • 1
Biruk Abebe
  • 2,235
  • 1
  • 13
  • 24