【问题标题】:Read uint8_t from std::stringstream as a numeric type从 std::stringstream 读取 uint8_t 作为数字类型
【发布时间】:2019-07-04 17:56:15
【问题描述】:

我的理解是从stringstream 读取uint8_t 是一个问题,因为stringstream 会将uint8_t 解释为char。我想知道如何将uint8_tstringstream 中读取为数字类型。比如下面的代码:

#include <iostream>
#include <sstream>

using namespace std;

int main()
{
    uint8_t ui;
    std::stringstream ss("46");
    ss >> ui;
    cout << unsigned(ui);
    return 0;
}

打印出52。我希望它打印出46

编辑:另一种方法是从stringstream 中读取string,然后将解决方案转换为uint8_t,但这会破坏良好的链接属性。例如,在我要写的实际代码中,我经常需要这样的东西:

   void foobar(std::istream & istream){
       uint8_t a,b,c;
       istream >> a >> b >> c;
       // TODO...
   }

【问题讨论】:

  • 读取两个字节并通过std::stoi解析它们?
  • 那是丑陋的解决方案。我知道。
  • @freakish - 不是 OP,而是创建一个字符串并自己解析它而不是流(它的工作)似乎关闭了。
  • 你的笔记对我来说没有意义——你可以阅读uint16_t,将其投射出来,再也不要使用它......当然,一些范围检查和流式传输状态也随之失效。
  • @bruno - 但是std::uint8_t 被宣传为数字类型,而不是字符,这是一个问题。

标签: c++


【解决方案1】:

经过多次反复,答案似乎是没有标准的方法来做到这一点。选项是将uint8_t 读取为uint16_tstd::string,然后将这些值转换为uint8_t

#include <iostream>
#include <sstream>

using namespace std;

int main()
{
    uint8_t ui;
    uint16_t tmp;
    std::stringstream ss("46");
    ss >> tmp;
    ui = static_cast<uint8_t>(tmp);
    cout << unsigned(ui);
    return 0;
}

但是,这样的解决方案忽略了范围检查。因此,如果需要,您将需要自己实现。

【讨论】:

    【解决方案2】:

    如果您想以格式化方式阅读,请不要使用charunsigned char(uint8_t)。您的示例代码及其结果是预期行为。

    我们可以从https://en.cppreference.com/w/cpp/io/basic_istream/operator_gtgt2看到

    template< class Traits >
    basic_istream<char,Traits>& operator>>( basic_istream<char,Traits>& st, unsigned char& ch );
    

    这会“执行字符输入操作”。

    52 是 '4' 的 ascii 代码。这意味着stringstream 只读取了一个字节,仍准备读取“6”。

    因此,如果您想以所需的方式工作,您应该sstream::operator&gt;&gt; 使用 2 字节或更大的整数类型,然后将其转换为 uint8_t - 您自己回答的确切方式.

    这里是这些重载的参考。 https://en.cppreference.com/w/cpp/io/basic_istream/operator_gtgt

    【讨论】:

    • 我不会真正称其为“答案”,但这是非常有用的信息。
    【解决方案3】:

    你可以为uint8_t重载输入operator&gt;&gt;,如:

    std::stringstream& operator>>(std::stringstream& str, uint8_t& num) {
       uint16_t temp;
       str >> temp;
       /* constexpr */ auto max = std::numeric_limits<uint8_t>::max();
       num = std::min(temp, (uint16_t)max);
       if (temp > max) str.setstate(std::ios::failbit);
       return str;
    }
    

    现场演示:https://wandbox.org/permlink/cVjLXJk11Gigf5QE

    说实话,我不确定这样的解决方案是否没有问题。更有经验的人可能会澄清。


    更新

    请注意,此解决方案通常不适用于std::basic_istream(以及它的实例std::istream),因为unsigned char 有一个重载的operator&gt;&gt;[istream.extractors]。然后,行为将取决于uint8_t 的实现方式。

    【讨论】:

    • 这看起来不错。如果你把它放在一个匿名的命名空间中,那么我想任何损害都会受到限制
    • 虽然我刚刚询问了字符串流,但我想您可以轻松地将 stringstream 更改为 istream,然后覆盖所有输入流。
    • @bremen_matt 好像没那么简单。看这里:wandbox.org/permlink/8a2kGAywWyJUfPzr。在通过 derived-to-base 转换将 strss 配对之前,编译器显然优先考虑 uichar简单隐式转换。不知道如何解决这个问题。
    • @DanielLangr “编译器显然优先考虑将 ui 简单的隐式转换为 char。” – 这种隐式转换不适用于引用。但是,流的std::operator&gt;&gt; 存在unsigned char &amp; 重载。我不确定是什么导致明显的歧义得到解决,有利于 std 版本,但高度怀疑 libstdc++ 中的以下专业化:extern template istream&amp; operator&gt;&gt;(istream&amp;, unsigned char&amp;); - 这表明对标准进行模棱两可的重载是一个非常糟糕的主意功能,我猜。
    • @ArneVogel 哦,现在明白你的意思了。我之前没有提到unsigned char 的重载(eel.is/c++draft/input.streams#istream.extractors)。这个解决方案可能真的很脆弱。我会在答案中添加注释。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-11-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-03
    相关资源
    最近更新 更多