Kon*_*lph 1 c++ io istream-iterator istream
我有一个表示字符序列的类,我想operator >>为它实现一个.我的实现目前看起来像这样:
inline std::istream& operator >>(std::istream& in, seq& rhs) {
std::copy(
std::istream_iterator<char>(in),
std::istream_iterator<char>(),
std::back_inserter(rhs));
// `copy` doesn't know when to stop reading so it always also sets `fail`
// along with `eof`, even if reading succeeded. On the other hand, when
// reading actually failed, `eof` is not going to be set.
if (in.fail() and in.eof())
in.clear(std::ios_base::eofbit);
return in;
}
Run Code Online (Sandbox Code Playgroud)
但是,以下可预测的失败:
std::istringstream istr("GATTACA FOO");
seq s;
assert((istr >> s) and s == "GATTACA");
Run Code Online (Sandbox Code Playgroud)
特别是,一旦我们到达" GATTACA FOO"中的空格,复制停止(预期)并将故障位置设置为istream(也是预期的).但是,就所seq涉及的而言,读取操作实际上是成功的.
我可以使用这种模型std::copy吗?我也想过用一个istreambuf_iterator而不是这个,但实际上并没有解决这个问题.
更重要的是,对输入" GATTACAFOO" 的读取操作应该失败,因为该输入不代表有效的DNA序列(这是我的类所代表的).另一方面,int从输入读取42foo实际上在C++中成功,所以也许我应该将每个有效前缀视为有效输入?
(顺便说一句,这对于显式循环来说相当简单,但我试图避免使用显式循环来支持算法.)
您不想这样,clear(eofbit)因为failbit如果由于达到EOF而读取失败,则应保持设置.否则,如果您只是离开eofbitset而没有failbit循环,例如while (in >> s)在到达EOF后将尝试另一次读取,然后该读取将failbit再次设置.除非它正在使用operator>>它,否则将清除它,并尝试再次阅读.然后再次.然后再次.流的正确行为是设置failbit是否由于EOF而读取失败,所以只需将其保留即可.
要使用迭代器和算法,你需要这样的东西
copy_while(InputIter, InputIter, OutputIter, Pred);
Run Code Online (Sandbox Code Playgroud)
只有在谓词为真时才复制输入序列,但标准库中不存在.你当然可以写一个.
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;
}
Run Code Online (Sandbox Code Playgroud)
现在你可以像这样使用它:
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");
}
Run Code Online (Sandbox Code Playgroud)
这工作,但问题是,istream_iterator使用operator>>读取字符,所以它跳过空白.这意味着"GATTACA"算法会消耗后续空间并将其丢弃,因此将其添加到末尾main会失败:
assert(istr.get() == ' ');
Run Code Online (Sandbox Code Playgroud)
要解决这个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;
}
Run Code Online (Sandbox Code Playgroud)
要完成此操作,您可能希望指示无法提取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;
}
Run Code Online (Sandbox Code Playgroud)
最终版本还使用了我最喜欢的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;
}
Run Code Online (Sandbox Code Playgroud)
哨兵将跳过前导空格,如果到达输入的末尾,它将设置eofbit.应该做的另一个改变是seq在将任何东西推入其中之前清空它,例如从rhs.clear()你的seq类型开始或等效.
| 归档时间: |
|
| 查看次数: |
316 次 |
| 最近记录: |