2014-01-13 55 views
16

在下面的程序如何安全地从流中读取一个unsigned int?

#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甚至没有打嗝,默默地producing a wrong result

11000 
10001 

我们需要能够赶上那些运行时类型不匹配错误。如果我们不是在模拟中捕捉到这一点的话,这可能会炸毁非常昂贵的硬件。

我们怎样才能安全地读取来自流的无符号的价值呢?

+1

看看的boost :: numerical_cast http://www.boost.org/doc/libs/1_55_0/libs/numeric/conversion/doc /html/boost_numericconversion/improved_numeric_cast__.html –

+2

哇,TIL,只有早上。 – chris

+2

该代码的输出结果应该是什么? – Nawaz

回答

8

你可以读入一个符号的类型,可以处理整个范围内第一和测试的变量,如果它为负或超出最大目标类型。如果您的无符号值可能不符合可用的最大签名类型,则必须使用除iostream之外的其他语法进行解析。

0

如下你可以这样做:

#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; 
} 
+0

这不适用于所有值。特别是'static_cast (-1)'的值会产生错误的结果(实际上最高位设置的任何东西)。 –

+0

@KonradRudolph'u> 0'? –

+0

@Bartek问题是它不是*错误。 –

4

首先,我想解析负值为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 longunsigned long long的减号开始的字符串。 do_put()覆盖可以简单地检查第一个字符,然后委托给基类版本,如果它不是'-'

下面是一个自定义方面的代码。虽然它有相当多的代码,但实际使用是相当直接的。大部分代码只是样板重写用于解析一个unsigned数(即,do_get()成员)的不同virtual功能。这些都是根据成员函数模板get_impl()执行的,它检查是否不再有字符或者下一个字符是否为'-'。在这两种情况下,转换都会失败,方法是将std::ios_base::failbit添加到参数err。否则,该函数仅委托给基类转换。

的相应创建面最终被用来构建一个新的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"); 
} 
+0

@BЈовић:我在手机上输入了原始答案。在手机上输入上面的代码可能会遇到拼写错误。我现在用一个工作示例更新了答案。 –

10

你可以写一个机械手:

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

如果在'-'之前有空格,这也可以工作吗? – stijn

+1

@stijn是格式化输入 –

相关问题