Although it isn't a priori obvious there is a relatively simple way to change what a stream considers to be whitespace. The way to do it is to imbue()
the stream with a std::locale
object whose std::ctype<char>
facet is replaced to consider the desired characters as whitespace. imbue()
, locale
, ctype
- huh?!? OK, well, these aren't necessarily the things you use day to day so here is a quick example which set's up std::cin
to use comma and newline characters as spaced:
#include <locale>
template <char S0, char S1>
struct commactype_base {
commactype_base(): table_() {
this->table_[static_cast<unsigned char>(S0)] = std::ctype_base::space;
this->table_[static_cast<unsigned char>(S1)] = std::ctype_base::space;
}
std::ctype<char>::mask table_[std::ctype<char>::table_size];
};
template <char S0, char S1 = S0>
struct ctype:
commactype_base<S0, S1>,
std::ctype<char>
{
ctype(): std::ctype<char>(this->table_, false) {}
};
Actually, this particular implementation of std::ctype<char>
can actually be used to use one or two arbitrary char
s as spaces (a proper C++2011 version would probably allow an arbitrary number of arguments; also, the don't really have to be template argumentss). Anyway, with this in place, just drop the following line at the beginning of your main()
function and you are all set:
std::cin.imbue(std::locale(std::locale(), new ::ctype<',', '
'>));
Note that this really only considers ,
and
as space characters. This also means that no other characters are skipped as whitespace. ... and, of course, a sequence of multiple comma characters is considered to be just one separator rather than possibly creating a bunch of empty strings. Also note that the above std::ctype<char>
facet removes all other character classification. If you want to parse other objects than just strings you might want to retain the other character classification and only change that for spaces. Here is a way this could be done:
template <char S0, char S1>
struct commactype_base {
commactype_base(): table_() {
std::transform(std::ctype<char>::classic_table(),
std::ctype<char>::classic_table() + std::ctype<char>::table_size,
this->table_,
[](std::ctype_base::mask m) -> std::ctype_base::mask {
return m & ~(std::ctype_base::space);
});
this->table_[static_cast<unsigned char>(S0)] |= std::ctype_base::space;
this->table_[static_cast<unsigned char>(S1)] |= std::ctype_base::space;
}
std::ctype<char>::mask table_[std::ctype<char>::table_size];
};
Sadly, this crashes with the version of gcc I have on my system (apparently the std::ctype<char>::classic_table()
yields a null pointer. Compiling this with a current version of clang doesn't work because clang doesn't support lambda. With the two caveats the above code should be correct, though...
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…