gpt4 book ai didi

c++ - 通过复制实现从流中读取

转载 作者:行者123 更新时间:2023-11-30 00:36:52 30 4
gpt4 key购买 nike

我有一个表示字符序列的类,我想为它实现一个运算符>>。我的实现目前看起来像这样:

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;
}

但是,以下可预见地失败了:

std::istringstream istr("GATTACA FOO");
seq s;
assert((istr >> s) and s == "GATTACA");

特别是,一旦我们到达“GATTACA FOO”中的空间,复制就会停止(预期)并在 istream 上设置 failbit(也是预期)。但是,就seq而言,读取操作实际上是成功的。

我可以使用 std::copy 对此建模吗?我还考虑过使用 istreambuf_iterator,但这实际上并不能解决这个特定问题。

此外,对输入“GATTACAFOO”的读取操作应该会失败,因为该输入不代表有效的 DNA 序列(这是我的类所代表的) .另一方面,从输入 42foo 中读取 int 实际上在 C++ 中成功 所以也许我应该将每个有效前缀视为有效输入?

(顺便说一下,如果使用显式循环,这会相当简单,但我正在努力避免使用显式循环以支持算法。)

最佳答案

你不想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类型。

关于c++ - 通过复制实现从流中读取,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14622792/

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com