2

I'm trying to loop a string word by word with the help of stringstream, here is my code:

string str = "hello world";
stringstream ss(str);
string word;
while (ss)
{
    ss >> word;
    cout << word << endl;
}

However, I got the result as below:

hello
world
world

Why did I get world twice?

Yves
  • 8,474
  • 7
  • 59
  • 114
  • 1
    `while(ss >> word) { ... }` Walk it through. You check if the stream is good, read, print, repeat. If the read fails the stream is bad but you print before you check it again and exit the loop. The right way is to only use the data you've read or written when the actual i/o operation succeeds. – Retired Ninja Dec 07 '18 at 03:58

2 Answers2

2

With this code snippet:

while (ss) { ... } 

You are checking the state of the string stream. If it contains valid data the loop will continue. This is why you are seeing the last word twice...

1st iteration of loop:

while ( ss ) {             // checks if there is valid data in ss and for errors (true)
    ss >> word;            // ss inserts "hello" into word.
    cout << word << endl;  // cout prints "hello" to the console.
}

2nd iteration of loop:

while ( ss ) {             // checks again if there is valid data (true)
    ss >> word;            // ss inserts "world" into word.
    cout << word << endl;  // cout prints "world" to the console.
}

3rd iteration of loop:

while ( ss ) {             // checks again if there is valid data (true)
    ss >> word;            // ss already contains "world", may optimize or copy over...
    cout << word << endl;  // cout prints "world" to the console.
}

4th iteration of loop:

while ( ss ) {             // ss encountered end of stream (false) exits loop.
    ss >> word;            // nothing inserted as loop has exited.
    cout << word << endl;  // nothing printed as loop has exited.
}

Instead of trying to use your stringstream as a condition for a loop try using the process of extracting data from the stringstream into a variable for your condition.

while( ss >> word ) {
    cout << word << endl;
}

1st iteration of loop:

while ( ss >> word ) {       // checks if ss inserted data into word
                             // ss inserts "hello" (true)
    cout << word << endl;    // cout prints "hello" to the console.
}

2nd iteration of loop:

while ( ss >> word ) {      // checks again if ss inserted data into word
                            // ss inserts "world" into word (true)
    cout << word << endl;   // cout prints "world" to the console.
}

3rd iteration of loop:

while ( ss >> word ) {      // checks again if ss inserted data into word
                            // ss fails to insert data (false) loop exits here
    cout << word << endl;   // nothing printed as loop exited
}
Greg Case
  • 43,435
  • 2
  • 24
  • 21
Francis Cugler
  • 7,462
  • 1
  • 24
  • 44
1
  • while (ss) sees that ss hasn't encountered a problem yet, so it runs the loop body. (That's what happens when you use ss as a boolean)
  • ss >> word; reads "hello"
  • cout << word << endl; prints "hello"
  • while (ss) sees that ss hasn't encountered a problem yet, so it runs the loop body again.
  • ss >> word; reads "world"
  • cout << word << endl; prints "world"
  • while (ss) sees that ss hasn't encountered a problem yet, so it runs the loop body again.
  • ss >> word; sees there is no more data, so it fails. word is unchanged, it still contains "world"
  • cout << word << endl; prints "world"
  • while (ss) sees that ss has encountered a problem and stops the loop.

You need to check whether to stop the loop after reading the word. For example, with:

while (true)
{
    ss >> word;
    if (!ss)
        break;
    cout << word << endl;
}

or for short:

while (ss >> word)
{
    cout << word << endl;
}
user253751
  • 45,733
  • 5
  • 44
  • 76