The role of std::ws (whitespace) when reading data

2019-05-29 03:36发布

问题:

Data saved in my file is (white spaces added at both beginning and end on purpose for this test):

1 2 3

Loading the data using the code below with or without "std::ws" does not cause any difference. So I am confused by the role of "std::ws" as I have seen code using it. Can someone explain a little bit? Thanks!

void main ()
{
ifstream inf; 
inf.open ("test.txt"); 

double x=0, y=0, z=0;
string line;

getline(inf, line);
istringstream iss(line);
//Using "std::ws" here does NOT cause any difference
if (!(iss >> std::ws >> x >> y >> z >> std::ws))
{
    cout << "Format error in the line" << endl;
}
else
{
    cout << x << y << z << endl;
}
iss.str(std::string ());
iss.clear();

cin.get();

}

回答1:

The primary use of std::ws is when switching between formatted and unformatted input:

  • formatted input, i.e., the usual input operators using `in >> value, skip leading whitespace and stop whenever the format is filled
  • unformatted input, e.g., std::getline(in, value) does not skip leading whitespace

For example, when reading an age and a fullname you might be tempted to read it like this:

 int         age(0);
 std::string fullname;
 if (std::cin >> age && std::getline(std::cin, fullname)) { // BEWARE: this is NOT a Good Idea!
     std::cout << "age=" << age << "  fullname='" << fullname << "'\n";
 }

However, if I'd enter this information using

47
Dietmar Kühl

It would print something like this

age=47 fullname=''

The problem is that the newline following the 47 is still present and immediately fills the std::getine() request. As a result you'd rather use this statement to read the data

if (std::cin >> age && std::getline(std::cin >> std::ws, fullname)) {
    ...
}

The use of std::cin >> std::ws skips the whitespace, in particular the newline, and carries on reading where the actual content is entered.



回答2:

By default, the stream's skipws bit is set and whitespace is automatically skipped before each input. If you unset it with iss >> std::noskipws, then you'll need iss >> std::ws later.

There are also times when whitespace is not automatically skipped. For example, to detect the end of the input, you can use if ( ( iss >> std::ws ).eof() ).



回答3:

skipws and noskipws are sticky but ws is NOT sticky, so if you want to skip whitespaces with ws you must use it before every operator>>.

Also note that skipws and noskipws only apply to formatted input operation performed with operator>> on the stream. but ws applies to both formatted input operation ( using operator>> ) and unformatted input operation (e.g. get, put, putback , .... )

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

int main ()
{
    char c;
    istringstream input( "     1test      2test       " );

    input >> skipws;
    c = input.peek();
    //skipws doesn't skip whitespaces in unformatted input
    cout << "After using skipws c = " << c << endl;

    input >> ws;
    c = input.peek();
    cout << "After using ws c = " <<  c << endl;
}

Output:

After using skipws c =  
After using ws c = 1