如何安全地从流中读取无符号int
How to safely read an unsigned int from a stream?
在以下程序中
#include <iostream>
#include <sstream>
int main()
{
std::istringstream iss("-89");
std::cout << static_cast<bool>(iss) << iss.good() << iss.fail() << iss.bad() << iss.eof() << 'n';
unsigned int u;
iss >> u;
std::cout << static_cast<bool>(iss) << iss.good() << iss.fail() << iss.bad() << iss.eof() << 'n';
return 0;
}
流lib将带符号的值读取到unsigned int
中,甚至没有停顿,默默地产生错误的结果:
11000
10001
我们需要能够捕获那些运行时类型不匹配的错误。如果我们没有在模拟中发现这一点,这可能会炸毁非常昂贵的硬件。
如何安全地从流中读取无符号值
您可以编写一个操纵器:
template <typename T>
struct ExtractUnsigned
{
T& value;
ExtractUnsigned(T& value) : value(value) {}
void read(std::istream& stream) const {
char c;
stream >> c;
if(c == '-') throw std::runtime_error("Invalid unsigned number");
stream.putback(c);
stream >> value;
}
};
template <typename T>
inline ExtractUnsigned<T> extract_unsigned(T& value) {
return ExtractUnsigned<T>(value);
}
template <typename T>
inline std::istream& operator >> (std::istream& stream, const ExtractUnsigned<T>& extract) {
extract.read(stream);
return stream;
}
int main()
{
std::istringstream data(" +89 -89");
unsigned u;
data >> extract_unsigned(u);
std::cout << u << 'n';
data >> extract_unsigned(u);
return 0;
}
您可以读取一个带符号类型的变量,该变量可以首先处理整个范围,并测试它是否为负数或超过目标类型的最大值。如果您的无符号值可能不适合可用的最大有符号类型,则必须使用iostream以外的其他方法进行解析。
首先,我认为为unsigned
值解析负值是错误的。该值由std::num_get<char>
根据strtoull()
的格式进行解码(22.4.2.12第3段,第3阶段,第二个项目符号)。strtoull()
的格式在C 7.22.1.4中定义为与C 6.4.4.1中整数常数的格式相同,这要求文字值可以用unsigned
类型表示。显然,负值不能用unsigned
类型来表示。诚然,我看了C11,它并不是C++11中引用的C标准。此外,在编译器中引用标准段落并不能解决这个问题。因此,下面是一种巧妙地改变值的解码的方法。
您可以设置一个全局std::locale
,其中std::num_get<...>
方面拒绝以减号开头的unsigned long
和unsigned long long
字符串。do_put()
重写可以简单地检查第一个字符,然后委托给基类版本(如果它不是'-'
)。
下面是一个自定义方面的代码。虽然这是一个相当多的代码,但实际使用是相当直接的。大多数代码只是覆盖用于解析unsigned
数字(即do_get()
成员)的不同virtual
函数的样板。这些都只是根据成员函数模板get_impl()
来实现的,该模板检查是否没有更多的字符或者下一个字符是否是'-'
。在这两种情况中的任何一种情况下,通过将std::ios_base::failbit
添加到参数err
,转换失败。否则,函数仅委托基类转换。
相应创建的facet最终用于构建新的std::locale
对象(custom
;请注意,当使用它的最后一个std::locale
对象被释放时,分配的positive_num_get
对象会自动释放)。安装此std::locale
将成为全局区域设置。全局区域设置由所有新创建的流使用。现有流,在示例std::cin
中,如果区域设置会影响它们,则需要为imbue()
d。一旦设置了全局语言环境,新创建的流将只接受更改后的解码规则,即不需要太多更改代码。
#include <iostream>
#include <sstream>
#include <locale>
class positive_num_get
: public std::num_get<char> {
typedef std::num_get<char>::iter_type iter_type;
typedef std::num_get<char>::char_type char_type;
// actual implementation: if there is no character or it is a '-' fail
template <typename T>
iter_type get_impl(iter_type in, iter_type end,
std::ios_base& str, std::ios_base::iostate& err,
T& val) const {
if (in == end || *in == '-') {
err |= std::ios_base::failbit;
return in;
}
else {
return this->std::num_get<char>::do_get(in, end, str, err, val);
}
}
// overrides of the various virtual functions
iter_type do_get(iter_type in, iter_type end,
std::ios_base& str, std::ios_base::iostate& err,
unsigned short& val) const override {
return this->get_impl(in, end, str, err, val);
}
iter_type do_get(iter_type in, iter_type end,
std::ios_base& str, std::ios_base::iostate& err,
unsigned int& val) const override {
return this->get_impl(in, end, str, err, val);
}
iter_type do_get(iter_type in, iter_type end,
std::ios_base& str, std::ios_base::iostate& err,
unsigned long& val) const override {
return this->get_impl(in, end, str, err, val);
}
iter_type do_get(iter_type in, iter_type end,
std::ios_base& str, std::ios_base::iostate& err,
unsigned long long& val) const override {
return this->get_impl(in, end, str, err, val);
}
};
void read(std::string const& input)
{
std::istringstream in(input);
unsigned long value;
if (in >> value) {
std::cout << "read " << value << " from '" << input << 'n';
}
else {
std::cout << "failed to read value from '" << input << 'n';
}
}
int main()
{
read("t 17");
read("t -18");
std::locale custom(std::locale(), new positive_num_get);
std::locale::global(custom);
std::cin.imbue(custom);
read("t 19");
read("t -20");
}
您可以按如下方式执行:
#include <iostream>
#include <sstream>
int main()
{
std::istringstream iss("-89");
std::cout << static_cast<bool>(iss) << iss.good() << iss.fail() << iss.bad() << iss.eof() << 'n';
int u;
if ((iss >> u) && (u > 0)) {
unsigned int u1 = static_cast<unsigned int>(u);
std::cout << "No errors: " << u1 << std::endl;
} else {
std::cout << "Error" << std::endl;
}
std::cout << static_cast<bool>(iss) << iss.good() << iss.fail() << iss.bad() << iss.eof() << 'n';
return 0;
}
- 在线程中读取无符号整数时,c++ 位是否以原子方式切换?
- 尝试将字符串从文件读取到无符号字符向量中
- ifstream 尝试读取 9 到 13 之间的无符号字符时非常奇怪的行为
- 从 C++ 中的无符号字符字节流中读取值
- C++ - 将任何文件的字节读取到无符号字符数组中
- 如何在 c++ 中读取/写入无符号数组到 ifstream/ostream?
- std::ifstream 将文件读取为 BYTE(无符号字符)而不是字符
- 用std :: stringstream读取/写作无符号字节
- 如何安全地从流中读取无符号int
- 正在尝试读取4字节无符号整数的二进制文件并转换为伏特
- 如何从文件中读取十六进制并将其存储在无符号字符中
- 如何从无符号变量写入和读取字节
- 使用 ifstream 从C++文件中读取二进制无符号短整型
- 如何从无符号字符(8位)缓冲区读取短整数(16位)
- 向二进制文件写入和读取无符号字符
- C++使用istringstream将int读取为无符号字符
- 如何使用 ifstream 正确从文件中读取无符号的 int 变量
- HDF5在磁盘上将32位无符号int写入64位,并读取32位无信号int
- 在无符号字符数组中读取充满字符串(用换行符分隔)的文件(反之亦然)的最快方法是什么?
- 通过无符号整数读取无符号字符数组未定义,因此不安全