1

文字シーケンスを表すクラスがあり、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;
}

ただし、次の場合は予想どおり失敗します。

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

特に、「<code>GATTACA FOO」のスペースに到達すると、コピーが停止し (予期される)、failbit が設定されますistream(これも予期されます)。ただし、読み取り操作は実際には成功しましたseq

を使用してこれをモデル化できますstd::copyか? 代わりにを使用することも考えistreambuf_iteratorましたが、これは実際にはこの特定の問題を解決しません。

さらに、入力 "<code>GATTACAFOO" に対する読み取り操作は失敗するはずです。その入力は有効な DNA 配列 (私のクラスが表すもの) を表していないからです。一方、int入力からの読み取りは42foo実際にはC++で成功するため、すべての有効なプレフィックスを有効な入力と見なす必要がありますか?

(ちなみに、これは明示的なループではかなり簡単ですが、アルゴリズムを優先して明示的なループを避けようとしています。)

4

2 に答える 2

5

EOFに達したために読み取りが失敗した場合は、を設定したままclear(eofbit)にする必要があるため、これは望ましくありません。failbitそれ以外の場合、eofbit設定をそのままにしておくと、EOFに達した後に別の読み取りを試行するfailbitなどのループが発生し、その読み取りが再度設定されます。それがあなたを使用していた場合を除いて、それはそれをクリアし、そしてもう一度読んでみてください。そしてまた。そしてまた。ストリームの正しい動作は、EOFが原因で読み取りに失敗した場合に設定することなので、設定したままにしておきます。while (in >> s)failbitoperator>>failbit

イテレータとアルゴリズムを使用してこれを行うには、次のようなものが必要です。

copy_while(InputIter, InputIter, OutputIter, Pred);

これは、述語がtrueの場合にのみ入力シーケンスをコピーしますが、標準ライブラリには存在しません。あなたは確かにそれを書くことができます。

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トリックの1つを使用して少し単純化しています。の2番目の引数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

于 2013-01-31T11:39:36.477 に答える
2

特に『GATTACA FOO』のスペースに到達するとコピーストップ(予想)

その仮定はすでに間違っています。代わりに、あなたは得られません

std::istringstream istr("GATTACA FOO");
seq s;
assert(!(istr >> s) && s == "GATTACAFOO");

istream_iterator<char>および標準アルゴリズムを使用したコピーは機能しcopyません。これは、ストリームの最後まで常に文字を抽出するためです。

終了条件に達し、終了条件が一致しない文字を抽出してはならない場合 (つまりin.peek()、streambuf を使用したり、直接見たりする場合) は、途中で終了するコピーが必要です。

これを行うにstd::copy()は、独自の特別な目的のストリーム イテレータ (終了条件が次の文字と一致する場合、終了イテレータと比較する) が必要になります。IMHO は、明示的なループよりもあいまいさを生み出します。YMMV

于 2013-01-31T10:54:13.537 に答える