您不想这样做,clear(eofbit)
因为failbit
如果由于到达 EOF 而读取失败,则应该保持设置。否则,如果您只是离开eofbit
设置而没有failbit
循环,例如while (in >> s)
将在到达 EOF 后尝试另一次读取,然后该读取将failbit
再次设置。除非它正在使用您的operator>>
,否则它将清除它,然后尝试再次阅读。然后再次。然后再次。failbit
如果由于 EOF 而读取失败,则流的正确行为是设置,因此只需保持设置即可。
要使用迭代器和算法做到这一点,您需要类似的东西
copy_while(InputIter, InputIter, OutputIter, Pred);
只有当谓词为真时才会复制输入序列,但这在标准库中不存在。你当然可以写一个。
template<typename InputIter, typename OutputIter, typename Pred>
OutputIter
copy_while(InputIter begin, InputIter end, OutputIter result, Pred pred)
{
while (begin != end)
{
typename std::iterator_traits<InputIter>::value_type value = *begin;
if (!pred(value))
break;
*result = value;
result++;
begin++;
}
return result;
}
现在你可以像这样使用它:
inline bool
is_valid_seq_char(char c)
{ return std::string("ACGT").find(c) != std::string::npos; }
inline std::istream&
operator>>(std::istream& in, seq& rhs)
{
copy_while(
std::istream_iterator<char>(in),
std::istream_iterator<char>(),
std::back_inserter(rhs),
&is_valid_seq_char);
return in;
}
int main()
{
std::istringstream istr("GATTACA FOO");
seq s;
assert((istr >> s) and s == "GATTACA");
}
这可行,但问题是istream_iterator
用于operator>>
读取字符,因此它会跳过空格。这意味着后面的空间"GATTACA"
被算法消耗并丢弃,因此将其添加到末尾main
会失败:
assert(istr.get() == ' ');
为了解决这个istreambuf_iterator
不跳过空格的使用:
inline std::istream&
operator>>(std::istream& in, seq& rhs)
{
copy_while(
std::istreambuf_iterator<char>(in),
std::istreambuf_iterator<char>(),
std::back_inserter(rhs),
&is_valid_seq_char);
return in;
}
要完成此操作,您可能希望指示提取失败,seq
如果没有提取字符:
inline std::istream&
operator>>(std::istream& in, seq& rhs)
{
copy_while( std::istreambuf_iterator<char>(in), {},
std::back_inserter(rhs), &is_valid_seq_char);
if (seq.empty())
in.setstate(std::ios::failbit); // no seq in stream
return in;
}
{}
最终版本还使用了我最喜欢的 C++11 技巧之一,通过使用结束迭代器来稍微简化它。的第二个参数的类型copy_while
必须与第一个参数的类型相同,推导为std::istreambuf_iterator<char>
,因此{}
简单的值初始化另一个相同类型的迭代器。
编辑:如果您想要更接近std::string
提取匹配,那么您也可以这样做:
inline std::istream&
operator>>(std::istream& in, seq& rhs)
{
std::istream::sentry s(in);
if (s)
{
copy_while( std::istreambuf_iterator<char>(in), {},
std::back_inserter(rhs), &is_valid_seq_char);
int eof = std::char_traits<char>::eof();
if (std::char_traits<char>::eq_int_type(in.rdbuf()->sgetc(), eof))
in.setstate(std::ios::eofbit);
}
if (rhs.empty())
in.setstate(std::ios::failbit);
return in;
}
哨兵将跳过前导空格,如果您到达输入的末尾,它将设置eofbit
。可能应该进行的另一个更改是seq
在将任何内容推入之前清空,例如,从您的类型开始rhs.clear()
或等效。seq