【问题标题】:How do I print bytes as hexadecimal?如何将字节打印为十六进制?
【发布时间】:2012-05-22 20:47:35
【问题描述】:

我知道在 C# 中你可以使用 String.Format 方法。但是你如何在 C++ 中做到这一点?是否有允许我将字节转换为十六进制的功能?只需要将一个 8 字节长的数据转换为 Hex,我该怎么做?

【问题讨论】:

  • sprintf() ?填料填料填料
  • @arrowdodger 对不起,是的,我想要它的十六进制

标签: c++ bytearray type-conversion data-conversion


【解决方案1】:

如果您想使用 C++ 流而不是 C 函数,您可以执行以下操作:

int ar[] = { 20, 30, 40, 50, 60, 70, 80, 90 };
const int siz_ar = sizeof(ar) / sizeof(int);

for (int i = 0; i < siz_ar; ++i)
    cout << ar[i] << " ";
cout << endl;

for (int i = 0; i < siz_ar; ++i)
    cout << hex << setfill('0') << setw(2) << ar[i] << " ";
cout << endl;

很简单。

输出:

20 30 40 50 60 70 80 90
14 1e 28 32 3c 46 50 5a 

【讨论】:

  • 值得注意的是,您应该 #include &lt;iomanip&gt; 并且 hexsetfillsetw 都在 std 命名空间中。此外,hex 是持久的,因此您可以将其拉出循环,并且您应该在完成后调用dec,以便流以十进制打印未来的整数值,假设这是您想要的。
  • 很好的答案 --- 它可以使用一些例子 hexsetfill 是什么。
  • 为了解释@Andrew,如果你有一个 char c 并且想要将 > 0x80 的值打印为十六进制,你需要将它转换为 (unsigned int)(unsigned char)c。否则,您将打印 32 位 2 的补码。
  • @DrewNoakes 我已尝试在以下位置介绍这些要点:stackoverflow.com/a/53673624/895245
  • @jbr unsigned char 不起作用。对于新人,如果你有char a = 20; cout &lt;&lt; hex &lt;&lt; a &lt;&lt; endl;,它会给你垃圾。因为“char 对带有operator&lt;&lt; 的ostream 有一个特殊的重载”。所以你需要转换为例如int,如cout &lt;&lt; hex &lt;&lt; (int)a &lt;&lt; endl;。至于打印值 > 0x80 为十六进制,还需要进行强制转换。原因是printing hexadecimal characters in c
【解决方案2】:

你可以一次将一个字节(无符号字符)转换成这样的数组

char buffer [17];
buffer[16] = 0;
for(j = 0; j < 8; j++)
    sprintf(&buffer[2*j], "%02X", data[j]);

【讨论】:

  • 我假设他想将一个数字数组转换为一个十六进制数字数组,我猜他可能想要一个十六进制字符串
  • 我会注意到这实际上是 C 方式,而不是 C++。我宁愿接受@Component 10 的回答。
  • 如果所讨论的字符为负数,可变参数函数的类型提升将导致六个前导“F”。
  • 你可能在 buffer[2*j] 之前错过了 &-char
  • @imallett 是的,绝对应该使用%hhxunsigned char。新人:printing hexadecimal characters in c
【解决方案3】:

C:

static void print_buf(const char *title, const unsigned char *buf, size_t buf_len)
{
    size_t i = 0;
    fprintf(stdout, "%s\n", title);
    for(i = 0; i < buf_len; ++i)
    fprintf(stdout, "%02X%s", buf[i],
             ( i + 1 ) % 16 == 0 ? "\r\n" : " " );

}

C++:

void print_bytes(std::ostream& out, const char *title, const unsigned char *data, size_t dataLen, bool format = true) {
    out << title << std::endl;
    out << std::setfill('0');
    for(size_t i = 0; i < dataLen; ++i) {
        out << std::hex << std::setw(2) << (int)data[i];
        if (format) {
            out << (((i + 1) % 16 == 0) ? "\n" : " ");
        }
    }
    out << std::endl;
}

【讨论】:

    【解决方案4】:

    在现代 C++ 中打印任意结构

    到目前为止,所有答案都只告诉您如何打印整数数组,但我们也可以打印任意结构,只要我们知道它的大小。下面的示例创建了这样的结构并通过其字节迭代指针,将它们打印到输出:

    #include <iostream>
    #include <iomanip>
    #include <cstring>
    
    using std::cout;
    using std::endl;
    using std::hex;
    using std::setfill;
    using std::setw;
    
    using u64 = unsigned long long;
    using u16 = unsigned short;
    using f64 = double;
    
    struct Header {
        u16 version;
        u16 msgSize;
    };
    
    struct Example {
        Header header;
        u64 someId;
        u64 anotherId;
        bool isFoo;
        bool isBar;
        f64 floatingPointValue;
    };
    
    int main () {
        Example example;
        // fill with zeros so padding regions don't contain garbage
        memset(&example, 0, sizeof(Example));
        example.header.version = 5;
        example.header.msgSize = sizeof(Example) - sizeof(Header);
        example.someId = 0x1234;
        example.anotherId = 0x5678;
        example.isFoo = true;
        example.isBar = true;
        example.floatingPointValue = 1.1;
    
        cout << hex << setfill('0');  // needs to be set only once
        auto *ptr = reinterpret_cast<unsigned char *>(&example);
        for (int i = 0; i < sizeof(Example); i++, ptr++) {
            if (i % sizeof(u64) == 0) {
                cout << endl;
            }
            cout << setw(2) << static_cast<unsigned>(*ptr) << " ";
        }
    
        return 0;
    }
    

    这是输出:

    05 00 24 00 00 00 00 00 
    34 12 00 00 00 00 00 00 
    78 56 00 00 00 00 00 00 
    01 01 00 00 00 00 00 00 
    9a 99 99 99 99 99 f1 3f
    

    请注意,此示例还说明了 memory alignment 的工作原理。我们看到version 占用 2 个字节 (05 00),然后是 msgSize,还有 2 个字节 (24 00),然后是 4 个字节的填充,之后是 someId (34 12 00 00 00 00 00 00) 和 anotherId (78 56 00 00 00 00 00 00)。然后isFoo,占1个字节(01)和isBar,再占一个字节(01),后面是6个字节的填充,最后以双字段floatingPointValue的IEEE 754标准表示结束。

    还请注意,所有值都表示为little endian(最低有效字节在前),因为这是在英特尔平台上编译和运行的。

    【讨论】:

      【解决方案5】:

      您可以使用 C++20 std::format 来实现,这与 C# 中的 String.Format 类似:

      std::string s = std::format("{:x}", std::byte(42)); // s == 2a
      

      std::format 广泛可用之前,您可以使用the {fmt} librarystd::format 基于 (godbolt):

      std::string s = fmt::format("{:x}", std::byte(42)); // s == 2a
      

      免责声明:我是 {fmt} 和 C++20 std::format 的作者。

      【讨论】:

        【解决方案6】:

        这是 Nibble to Hex 方法的修改版本

        void hexArrayToStr(unsigned char* info, unsigned int infoLength, char **buffer) {
            const char* pszNibbleToHex = {"0123456789ABCDEF"};
            int nNibble, i;
            if (infoLength > 0) {
                if (info != NULL) {
                    *buffer = (char *) malloc((infoLength * 2) + 1);
                    buffer[0][(infoLength * 2)] = 0;
                    for (i = 0; i < infoLength; i++) {
                        nNibble = info[i] >> 4;
                        buffer[0][2 * i] = pszNibbleToHex[nNibble];
                        nNibble = info[i] & 0x0F;
                        buffer[0][2 * i + 1] = pszNibbleToHex[nNibble];
                    }
                } else {
                    *buffer = NULL;
                }
            } else {
                *buffer = NULL;
            }
        }
        

        【讨论】:

          【解决方案7】:

          我不知道比以下更好的方法:

          unsigned char byData[xxx]; 
          
          int nLength = sizeof(byData) * 2;
          char *pBuffer = new char[nLength + 1];
          pBuffer[nLength] = 0;
          for (int i = 0; i < sizeof(byData); i++)
          {
              sprintf(pBuffer[2 * i], "%02X", byData[i]);
          }
          

          您可以使用 Nibble to Hex 方法加快速度

          unsigned char byData[xxx];
          
          const char szNibbleToHex = { "0123456789ABCDEF" };
          
          int nLength = sizeof(byData) * 2;
          char *pBuffer = new char[nLength + 1];
          pBuffer[nLength] = 0;
          for (int i = 0; i < sizeof(byData); i++)
          {
              // divide by 16
              int nNibble = byData[i] >> 4;
              pBuffer[2 * i]  = pszNibbleToHex[nNibble];
          
              nNibble = byData[i] & 0x0F;
              pBuffer[2 * i + 1]  = pszNibbleToHex[nNibble];
          
          }
          

          【讨论】:

          • 使用sprintf() 通常被认为是危险的,这是有充分理由的。在这里,由于您仔细分配了足够的空间,因此使用还可以,但总的来说,我会避免使用它。我更喜欢轻咬法。
          • 应该是const char* szNibbleToHex
          • 或者应该是 const char szNibbleToHex[16] = { "0123456789ABCDEF" };
          【解决方案8】:

          另一个答案,如果字节数组被定义为char[],大写并用空格分隔。

          void debugArray(const unsigned char* data, size_t len) {
              std::ios_base::fmtflags f( std::cout.flags() );
              for (size_t i = 0; i < len; ++i)
                  std::cout << std::uppercase << std::hex << std::setfill('0') << std::setw(2) << (((int)data[i]) & 0xFF) << " ";
              std::cout << std::endl;
              std::cout.flags( f );
          }
          

          例子:

          unsigned char test[]={0x01, 0x02, 0x03, 0x04, 0x05, 0x06};
          debugArray(test, sizeof(test));
          

          输出:

          01 02 03 04 05 06

          【讨论】:

            【解决方案9】:

            使用 C++ 流并在之后恢复状态

            这是How do I print bytes as hexadecimal? 的变体,但是:

            main.cpp

            #include <iomanip>
            #include <iostream>
            
            int main() {
                int array[] = {0, 0x8, 0x10, 0x18};
                constexpr size_t size = sizeof(array) / sizeof(array[0]);
            
                // Sanity check decimal print.
                for (size_t i = 0; i < size; ++i)
                    std::cout << array[i] << " ";
                std::cout << std::endl;
            
                // Hex print and restore default afterwards.
                std::ios cout_state(nullptr);
                cout_state.copyfmt(std::cout);
                std::cout << std::hex << std::setfill('0') << std::setw(2);
                for (size_t i = 0; i < size; ++i)
                    std::cout << array[i] << " ";
                std::cout << std::endl;
                std::cout.copyfmt(cout_state);
            
                // Check that cout state was restored.
                for (size_t i = 0; i < size; ++i)
                    std::cout << array[i] << " ";
                std::cout << std::endl;
            }
            

            编译运行:

            g++ -o main.out -std=c++11 main.cpp
            ./main.out
            

            输出:

            0 8 16 24 
            00 8 10 18 
            0 8 16 24
            

            在 Ubuntu 16.04、GCC 6.4.0 上测试。

            【讨论】:

            • 这不是线程安全的。这可能会或可能不会重要,具体取决于用例。不过我认为至少值得一提。
            • 哦,它也不是异常安全的。这就是为什么你链接到的页面有better alternatives ;)
            【解决方案10】:

            另一个 C++17 替代方案,因为为什么不呢!

            std::cout<<std::hex<<std::setfill('0');
            
            struct {
                std::uint16_t first{666};
                std::array<char,4> second{'a','b','c','d'};
            } my_struct;
            
            auto ptr = reinterpret_cast<std::byte*>(&my_struct);
            auto buffer = std::vector<std::byte>(ptr, ptr + sizeof(my_struct));
            std::for_each(std::begin(buffer),std::end(buffer),[](auto byte){
                std::cout<<std::setw(2)<<std::to_integer<int>(byte)<<' ';
            });
            

            可执行代码here.

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2014-03-07
              • 1970-01-01
              • 1970-01-01
              • 2015-02-14
              • 2013-01-18
              • 1970-01-01
              相关资源
              最近更新 更多