问题
I was trying out the noskipws manipulator in C++ and I wrote following code.
#include <iostream>
#include <sstream>
#include <string>
using namespace std;
int main()
{
string first, middle, last;
istringstream("G B Shaw") >> first >> middle >> last;
cout << "Default behavior: First Name = " << first << ", Middle Name = " << middle << ", Last Name = " << last << '\n';
istringstream("G B Shaw") >> noskipws >> first >> middle >> last;
cout << "noskipws behavior: First Name = " << first << ", Middle Name = " << middle << ", Last Name = " << last << '\n';
}
I expect the following output:
Expected Output
Default behavior: First Name = G, Middle Name = B, Last Name = Shaw
noskipws behavior: First Name = G, Middle Name = , Last Name = B
Output
Default behavior: First Name = G, Middle Name = B, Last Name = Shaw
noskipws behavior: First Name = G, Middle Name = , Last Name = Shaw
I modified this code to make it work for chars like this and it works perfectly fine.
#include <iostream>
#include <sstream>
#include <string>
using namespace std;
int main()
{
char first, middle, last;
istringstream("G B S") >> first >> middle >> last;
cout << "Default behavior: First Name = " << first << ", Middle Name = " << middle << ", Last Name = " << last << '\n';
istringstream("G B S") >> noskipws >> first >> middle >> last;
cout << "noskipws behavior: First Name = " << first << ", Middle Name = " << middle << ", Last Name = " << last << '\n';
}
I know how cin works and I wasn't able to figure out why it works this way in case of string
.
回答1:
std::istringstream("G B S") >> std::noskipws >> first >> middle >> last;
When an extraction is performed on strings, the string is first cleared and characters are inserted into its buffer.
21.4.8.9 Inserters and extractors
template<class charT, class traits, class Allocator> basic_istream<charT, traits>& operator>>(basic_istream<charT, traits>& is, basic_string<charT, traits, Allocator>& str);
Effects: Behaves as a formatted input function (27.7.2.2.1). After constructing a sentry object, if the sentry converts to
true
, callsstr.erase()
and then extracts characters fromis
and appends them tostr
as if by callingstr.append(1, c).
[...]
The first read will extract the string "G"
into first
. For the second extraction, nothing will be extracted because the std::noskipws
format flag is set, disabling the clearing of leading whitespace. Because of this, the string is cleared and then the extraction fails because no characters were put in. Here is the continuation of the above clause:
21.4.8.9 Inserters and extractors (Cont.)
[...] Characters are extracted and appended until any of the following occurs:
n
characters are stored;end-of-file occurs on the input sequence;
isspace(c, is.getloc())
is true for the next available input characterc
.
When the stream determines a failed extraction the std::ios_base::failbit
is set in the stream state indicating an error.
From this point on any and all attempts at I/O will fail unless the stream state is cleared. The extractor becomes inoperable and it will not run given a stream state not cleared of all its errors. This means that the extraction into last
doesn't do anything and it retains the value it had at the previous extraction (the one without std::noskipws
) because the stream did not clear the string.
As for the reason why using char
works: Characters have no formatting requirements in C or C++. Any and all characters can be extracted into a object of type char
, which is the reason why you're seeing the correct output despite std::noskipws
being set:
27.7.2.2.3/1 [istream::extractors]
template<class charT, class traits> basic_istream<charT, traits>& operator>>(basic_istream<charT, traits>& in, charT& c);
Effects: Behaves like a formatted input member (as described in 27.7.2.2.1) of in. After a sentry object is constructed a character is extracted from
in
, if one is available, and stored inc
. Otherwise, the function callsin.setstate(failbit)
.
The semantics for the extractor will store a character into its operand if one is available. It doesn't delimit upon whitespace (or even the EOF character!). It will extract it just like a normal character.
回答2:
The basic algorithm for >>
of a string is:
1) skip whitespace
2) read and extract until next whitespace
If you use noskipws
, then the first step is skipped
.
After the first read, you are positionned on a whitespace, so the next (and all following) reads will stop immediatly, extracting nothing.
For more information you can see this.
Form cplusplus.com ,
many extraction operations consider the whitespaces themselves as the terminating character, therfore, with the skipws flag disabled, some extraction operations may extract no characters at all from the stream.
So , remove the noskipws , when using with strings .
回答3:
The reason is that in the second example you are not reading into last variable at all and instead you are printing old value of it.
std::string first, middle, last;
std::istringstream iss("G B S");
^^^
iss >> first >> middle >> last;
std::cout << "Default behavior: First Name = " << first
<< ", Middle Name = " << middle << ", Last Name = " << last << '\n';
std::istringstream iss2("G B T");
^^^
iss2 >> std::noskipws >> first >> middle >> last;
std::cout << "noskipws behavior: First Name = " << first
<< ", Middle Name = " << middle << ", Last Name = " << last << '\n';
Default behavior: First Name = G, Middle Name = B, Last Name = S
noskipws behavior: First Name = G, Middle Name = , Last Name = S
This happen because after second read to variable last stream is positioned on whitespace.
来源:https://stackoverflow.com/questions/21809838/demonstration-of-noskipws-in-c