c++iomanip

noskipws on char extraction from StringStream causes it to not work with subsequent string extractions


Let me make this simple. Why does the program below write "Thi" instead of "Thisisatest."? I expect it to write the first two characters in the first loop, and then write the rest of the test string without whitespace.

#include <sstream>
#include <iostream>
using namespace std;

int main() {
  string test = "This is just a test.";
  stringstream ss(test);
  
  char c;
  int i = 0;
  while (ss >> noskipws >> c) { 
    int ascii = (int)c;
    if (i > 2) { // break after writing 2 characters
      break;
    }
    i++;
    cout << c; 
  }

  // continue using the same sstring, 
  // but with a string instead of a char
  // notice that there is no noskipws here
  string s;
  while(ss >> s) {
    cout << s; 
  }

  // program exits with only "Thi" printed
  return 0;
}

Interestingly enough, removing >> noskipws fixes the problem. Why though?


Solution

  • ss >> s means read in string s, and stop when you encounter whitespace.

    I added a pair of extra output statements to your program, one at the end of each loop. After the first loop ends, the input stream is positioned to input a space as its next character.

    When the program tries to input string s, it encounters whitespace immediately, and because you configured the stream to not skip leading whitespace, it stops, failing to input string s. That failure causes the stream to be put into a failed state.

    #include <sstream>
    #include <string>
    #include <iostream>
    using namespace std;
    
    int main() {
        string test = "This is just a test.";
        stringstream ss(test);
    
        char c;
        int i = 0;
        while (ss >> noskipws >> c) {
            int ascii = (int)c;
            if (i > 2) { // break after writing 2 characters
                break;
            }
            i++;
            cout << c;
        }
        std::cout << boolalpha 
            << "\nAfter first loop: " 
            << "\n  ss.fail() : " << ss.fail() 
            << "\n  c         : \'" << c << '\''
            << "\n";
    
        // continue using the same sstring, 
        // but with a string instead of a char
        // notice that there is no noskipws here
        string s{ "value before ss >> s" };
        while (ss >> s) {
            cout << s;
        }
        std::cout << "\nAfter second loop: "
            << "\n  ss.fail() : " << ss.fail()
            << "\n  s         : \"" << s << '\"'
            << '\n';
    
        // program exits with only "Thi" printed
        return 0;
    }
    

    Here is the output:

    Thi
    After first loop:
      ss.fail() : false
      c         : 's'
    
    After second loop:
      ss.fail() : true
      s         : ""