我有以下形式的序列号类:
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;
}
答案 0 :(得分:3)
标准格式化输入功能始终遵循相同的模式:
std::sentry
对象,根据std::ios_base::skipws
格式化标志的设置处理任何前导空格的跳过。std::ios_base::failbit
已设置,则读取值不会更改。也就是说,输入函数看起来像这样:
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
标志(如果在流上启用了异常,则在设置错误位时可能会发生异常)。