在 IOStreams 中,字符串(即 C 字符串和 C++ 字符串)几乎没有格式要求。仅在找到空白字符或捕获流的末尾之前,才会将任何和所有字符提取到字符串中。在您的示例中,您使用的字符串旨在吃掉重要数据之间的逗号,但您遇到的输出是我刚刚解释的行为的结果:dummy
字符串不仅吃掉逗号,而且字符序列的其余部分,直到下一个空白字符。
为避免这种情况,您可以将 achar
用于虚拟变量,它只有一个字符的空间。如果你想Apple 1
放入一个字符串,你将需要一个未格式化的提取,因为格式化的提取器operator>>()
只读取直到空白。此处使用的适当功能是std::getline()
:
string c;
char dummy;
if ((stream >> a >> dummy >> b >> dummy) &&
std::getline(stream >> std::ws, s))
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
{
}
在格式化提取后清除换行符也是必要的,这就是我用来std::ws
清除前导空格的原因。我还使用一个if
语句来包含提取以判断它是否成功。
任何更顺畅的方法都会对我有很大帮助。
std::ctype<char>
您可以使用流中包含的语言环境的方面将逗号字符的分类设置为空白字符。这将使虚拟变量的使用变得不必要。这是一个例子:
namespace detail
{
enum options { add, remove };
class ctype : public std::ctype<char>
{
private:
static mask* get_table(const std::string& ws, options opt)
{
static std::vector<mask> table(classic_table(),
classic_table() + table_size);
for (char c : ws)
{
if (opt == add)
table[c] |= space;
else if (opt == remove)
table[c] &= ~space;
}
return &table[0];
}
public:
ctype(const std::string& ws, options opt)
: std::ctype<char>(get_table(ws, opt)) { }
};
}
class adjustws_impl
{
public:
adjustws_impl(const std::string& ws, detail::options opt) :
m_ws(ws),
m_opt(opt)
{ }
friend std::istream& operator>>(std::istream& is,
const adjustws_impl& manip)
{
const detail::ctype* facet(new detail::ctype(manip.m_ws, manip.m_opt));
if (!std::has_facet<detail::ctype>(is.getloc())
{
is.imbue(std::locale(is.getloc(), facet));
} else
delete facet;
return is;
}
private:
std::string m_ws;
detail::options m_opt;
};
adjustws_impl setws(const std::string& ws)
{
return adjustws_impl(ws, detail::add);
}
adjustws_impl unsetws(const std::string& ws)
{
return adjustws_impl(ws, detail::remove);
}
int main()
{
std::istringstream iss("10,1.546,Apple 1");
int a; double b; std::string c;
iss >> setws(","); // set comma to a whitespace character
if ((iss >> a >> b) && std::getline(iss >> std::ws, c))
{
// ...
}
iss >> unsetws(","); // remove the whitespace classification
}