2

次の形式のシリアル番号クラスがあります。

class SerialNumber { ... }

そして私はそれのために書きたいですoperator>>

istream& operator>>(istream& i, SerialNumber& s)
{
    ???

    return i;
}

シリアル番号は常に19文字の長さで、16進数で始まります。

私はistream.read19文字である必要があるかどうか混乱しています。プレフィックスの空白が含まれる場合があります。?

または、を読んでi >> std::stringから、19文字の長さであることを確認する必要があるかどうか。あなたがそれを読むとき、std::stringそれは空白をスキップします(それを実装する標準的な方法はありますか?)さらに私がそれを読むならば、std::stringそれは有効な19文字のシリアル番号プレフィックスを持っているかもしれません、そして私は入力を「読み過ぎ」ているかもしれません。?

アップデート:

inline istream& operator>>(istream& is, SerialNumber& id)
{
    ostringstream os;

    is >> ws;

    for (int i = 0; i < 19; i++)
    {
        char c;
        is >> c;
        os << c;
    }

    id = DecodeId(os.str());

    return is;
}

DietmarKühlコードの部分的にサニタイズされたバージョン:

istream& operator>> (istream& in, SerialNumber& sn)
{
    constexpr size_t n = 19;

    istream::sentry se(in);

    if (!se)
        return in;

    istreambuf_iterator<char> it(in.rdbuf()), end;

    if (it == end || !isxdigit(*it))
    {
        in.setstate(ios_base::failbit);
        return in;
    }

    string s(n,'?');
    for (size_t i = 0; it != end && i < n && !isspace(char(*it)), ++i)
            s[i] = *it++;

    sn = DecodeId(s);

    if (failed to decode)
        in.setstate(ios_base::failbit);

    return in;
}
4

2 に答える 2

3

標準形式の入力関数は常に同じパターンに従います。

  1. それらは、フォーマットフラグstd::sentryの設定に応じて、先頭の空白のスキップを処理するオブジェクトを作成することから始まります。std::ios_base::skipws
  2. 値の読み取りが何らかの方法で失敗して設定された場合、読み取り値は変更されませんstd::ios_base::failbit
  3. 文字は、フォーマットと一致しない最初の文字まで消費されます。

つまり、入力関数は次のようになります。

std::istream& operator>> (std::istream& in, SerialNumber& s) {
    std::istream::sentry kerberos(in);
    if (kerberos) {
        std::istreambuf_iterator<char> it(in.rdbuf()), end;
        char buffer[20] = {};
        int  i(0);
        if (it != end && std::isxdigit(static_cast<unsigned char>(*it))) {
            for (; it != end && i != 19
                   && !std::isspace(static_cast<unsigned char>(*it)); ++i) {
                buffer[i] = *it++;
            }
        }
        if (i == 19) {
            SerialNumber(buffer).swap(s);
        }
        else {
            in.setstate(std::ios_base::failbit);
        }
    }
    return in;
}
于 2012-12-27T06:19:48.447 に答える
1

一度に1ステップずつ実行する必要があります。

  • 常に空白をスキップしたい場合は、最初に。を実行しi >> std::wsます。ストリームにskipwsフラグが設定されていない可能性があります。それ以外の場合は、ユーザーが空白をスキップするかどうかを決定し、空白を読み取るときにストリームエラービットを設定します。

  • 最初の数字を読んでchar、16進数かどうかを確認します。そうでない場合は、ストリームエラービットを設定します。

  • 残りの18文字を読み取り、シリアル番号の形式に合わない文字を見つけたらすぐに、ストリームエラービットを設定します。

    これを無効skipwsにする必要があります。無効にしないと、空白で区切られた文字から有効な結果が得られます。その場合は、関数を終了するときに必ずskipwsフラグを復元してください(ストリームで例外が有効になっている場合は、エラービットを設定するときに例外を介して発生する可能性があります)。

于 2012-12-27T05:58:23.430 に答える