在处理空格作为分隔符时,使用std::istream_iterator<T>的明显答案已经给出,并得到了很多支持。当然,元素可能不是用空格分隔,而是用一些分隔符分隔。我没有找到任何答案,只是重新定义了空格的含义,将其称为分隔符,然后使用常规方法。
要改变流对空白的看法,只需使用(std::istream::imbue())将流的std::locale更改为std::ctype<char>方面,并定义空白的含义(也可以对std:::ctype<wchar_t>进行更改,但实际上略有不同,因为std::actype<char>是表驱动的,而std::type<wchar_t>是由虚拟函数驱动的)。
#include <iostream>
#include <algorithm>
#include <iterator>
#include <sstream>
#include <locale>
struct whitespace_mask {
std::ctype_base::mask mask_table[std::ctype<char>::table_size];
whitespace_mask(std::string const& spaces) {
std::ctype_base::mask* table = this->mask_table;
std::ctype_base::mask const* tab
= std::use_facet<std::ctype<char>>(std::locale()).table();
for (std::size_t i(0); i != std::ctype<char>::table_size; ++i) {
table[i] = tab[i] & ~std::ctype_base::space;
}
std::for_each(spaces.begin(), spaces.end(), [=](unsigned char c) {
table[c] |= std::ctype_base::space;
});
}
};
class whitespace_facet
: private whitespace_mask
, public std::ctype<char> {
public:
whitespace_facet(std::string const& spaces)
: whitespace_mask(spaces)
, std::ctype<char>(this->mask_table) {
}
};
struct whitespace {
std::string spaces;
whitespace(std::string const& spaces): spaces(spaces) {}
};
std::istream& operator>>(std::istream& in, whitespace const& ws) {
std::locale loc(in.getloc(), new whitespace_facet(ws.spaces));
in.imbue(loc);
return in;
}
// everything above would probably go into a utility library...
int main() {
std::istringstream in("a, b, c, d, e");
std::copy(std::istream_iterator<std::string>(in >> whitespace(", ")),
std::istream_iterator<std::string>(),
std::ostream_iterator<std::string>(std::cout, "\n"));
std::istringstream pipes("a b c| d |e e");
std::copy(std::istream_iterator<std::string>(pipes >> whitespace("|")),
std::istream_iterator<std::string>(),
std::ostream_iterator<std::string>(std::cout, "\n"));
}
大部分代码用于打包提供软分隔符的通用工具:合并一行中的多个分隔符。无法生成空序列。当流中需要不同的分隔符时,您可能会使用共享流缓冲区使用不同的设置流:
void f(std::istream& in) {
std::istream pipes(in.rdbuf());
pipes >> whitespace("|");
std::istream comma(in.rdbuf());
comma >> whitespace(",");
std::string s0, s1;
if (pipes >> s0 >> std::ws // read up to first pipe and ignore sequence of pipes
&& comma >> s1 >> std::ws) { // read up to first comma and ignore commas
// ...
}
}