为序列号写流'运算符>>`?

时间:2012-12-27 05:54:27

标签: c++ c++11

我有以下形式的序列号类:

class SerialNumber { ... }

我想为它写operator>>

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

    return i;
}

序列号始终为19个字符,以十六进制数字开头。

如果我应该istream.read 19个字符,我很困惑。它可能包括前缀空格。 ?

或者我是否应该阅读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;
}

2 个答案:

答案 0 :(得分:3)

标准格式化输入功能始终遵循相同的模式:

  1. 他们开始构建一个std::sentry对象,根据std::ios_base::skipws格式化标志的设置处理任何前导空格的跳过。
  2. 如果读取值以任何方式失败并且std::ios_base::failbit已设置,则读取值不会更改。
  3. 字符消耗的第一个字符与格式不匹配。
  4. 也就是说,输入函数看起来像这样:

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

答案 1 :(得分:1)

你应该一步一步:

  • 如果您希望始终跳过空格,请先执行i >> std::ws。流可能没有设置skipws标志。否则,让用户决定是否跳过空格,并在读取空格时设置流错误位。

  • 读取第一个char,看看它是否为十六进制数字。如果不是,则设置流错误位。

  • 读取剩下的18个字符,一旦找到不符合序列号格式的字符,就设置流错误位。

    您应该为此禁用skipws,否则您将从由空格分隔的字符中获得有效结果。如果这样做,那么请确保在退出函数时恢复skipws标志(如果在流上启用了异常,则在设置错误位时可能会发生异常)。