C++ 十六进制解析

Posted

技术标签:

【中文标题】C++ 十六进制解析【英文标题】:C++ Hex Parsing 【发布时间】:2010-12-30 08:40:54 【问题描述】:

我想知道如何将十六进制字符串转换为人类可读的字符串(如果这有意义的话),这将是我第一次真正遇到十六进制值,所以我仍在学习它们以及如何管理它们。

我有一个程序正在从包含原始数据包数据(十六进制)的文件中读取数据,我需要解析这些信息以便人类可读。

我需要做的一个例子是这个网站所做的http://home2.paulschou.net/tools/xlate/,您可以在其中输入十六进制并将其转换为文本。

【问题讨论】:

澄清一下:你想获取二进制数据并以十六进制格式显示? 我想获取以十六进制存储的原始数据包数据,并将其从十六进制转换为人类可读的格式。 原始数据本质上是二进制的,十六进制只是一种更简单的读取方式,因为每四个二进制数字都可以读取为一个十六进制数字。 我需要将以下格式的数据转换为:0000008f74d89d0fe4caca7f585f35049 到它的真实值 该网站有几个转换选项。你能在源代码中给出一个输入数据的例子,然后是一个你想看到的输出例子吗? “f(...)应该输出……”,省略号填写。 【参考方案1】:

取自C++ String Toolkit Library (StrTk) 库,以下内容就足够了。注意 out 应该指向一块大小为 std::distance(begin,end) 一半的内存,并且 [begin,end) 范围内的值是 0-9A-F 或 0-9a-f

inline bool convert_hex_to_bin(const unsigned char* begin, 
                               const unsigned char* end, 
                               unsigned char* out)
    
       if (std::distance(begin,end) % 2)
          return false;
       static const std::size_t symbol_count = 256;
       static const unsigned char hex_to_bin[symbol_count] = 
                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x00 - 0x07
                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x08 - 0x0F
                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x10 - 0x17
                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x18 - 0x1F
                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x20 - 0x27
                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x28 - 0x2F
                    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // 0x30 - 0x37
                    0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x38 - 0x3F
                    0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00, // 0x40 - 0x47
                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x48 - 0x4F
                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x50 - 0x57
                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x58 - 0x5F
                    0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00, // 0x60 - 0x67
                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x68 - 0x6F
                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x70 - 0x77
                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x78 - 0x7F
                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x80 - 0x87
                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x88 - 0x8F
                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x90 - 0x97
                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x98 - 0x9F
                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xA0 - 0xA7
                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xA8 - 0xAF
                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xB0 - 0xB7
                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xB8 - 0xBF
                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xC0 - 0xC7
                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xC8 - 0xCF
                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xD0 - 0xD7
                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xD8 - 0xDF
                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xE0 - 0xE7
                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xE8 - 0xEF
                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xF0 - 0xF7
                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  // 0xF8 - 0xFF
                  ;

       const unsigned char* itr = begin;
       while (end != itr)
       
          (*out)  = static_cast<unsigned char>(hex_to_bin[*(itr++)] << 4);
          (*out) |= static_cast<unsigned char>(hex_to_bin[*(itr++)]     );
          ++out;
       
       return true;
    

【讨论】:

【参考方案2】:

获取包含给定数字的十六进制表示的字符串的 C++-ish 方法是对流使用 hex 修饰符,如下例所示:

const int i = 0xdeadbeef;
cout << "0x" << hex << i << endl; // prints "0xdeadbeef"

您可以在字符串流上使用相同的修饰符,以防您需要在字符串变量中使用十六进制表示:

const int i = 0xdeadc0de;
ostringstream stream;
stream << "0x" << hex << i;

const string s = stream.str(); // s now contains "0xdeadc0de"

更新:

如果您的输入数据以包含字符串字符的十六进制表示的字符串形式给出,您需要知道输入字符串的编码才能正确显示。在最简单的情况下,字符串类似于 ASCII,它将一个字节映射到一个字符。因此,在给定的输入“414243”中,每两个字符(“41”、“42”、“43”)映射到一个 ASCII 值(65、66、67),后者映射到一个字符(“A”、“B” , "C")。

以下是 C++ 中的方法:

const string hexData = "414243";

assert( hexData.size() % 2 == 0 );

ostringstream asciiStream;
istringstream hexDataStream( hexData );
vector<char> buf( 3 ); // two chars for the hex char, one for trailing zero
while ( hexDataStream.good() ) 
    hexDataStream.get( &buf[0], buf.size() );
    if ( hexDataStream.good() ) 
        asciiStream << static_cast<char>( std::strtol( &buf[0], 0, 16 ) );
    


const string asciiData = asciiStream.str(); // asciiData == "ABC"

使用&lt;cstdlib&gt; 中的std::strtol 可以轻松实现;如果您坚持为此使用模板类,请使用 std::stringstream 将单个子字符串(如“41”)转换为十进制值(65)。

【讨论】:

我有点困惑,似乎只是将十六进制存储到一个字符串中?我需要将十六进制数据从其十六进制数据转换为可读字符串,例如home2.paulschou.net/tools/xlate那种网站。 它给出了一个数字的十六进制表示,对。 “十六进制数据”是什么意思?输入数据是带有字符“414243”的字符串,并且您希望将其转换为例如“ABC”(因为网站就是这样做的)? 是的,我想将其转换为“ABC”这1062000000000002000100024177616b656e65642d4465760036372e3232382e35302e3232333a38303835000000000009022c010000576f575472616e63652d4177616b656e696e670036372e3232382e34392e39303a3830383500000000000a包含了一些字符串,我从得到了数据的服务器的IP地址,我希望能够将上述数据转换成我可以读取它保存的值的格式。 这与我之前尝试做的类似,但由于某种原因它产生了相同的结果,它似乎在打印出来时只显示前两个字符,我不确定为什么字符串的长度明显大于打印出来的长度,我正在解析的数据是否包含干扰打印输出的终止字符? 如果我在更新的代码中使用您评论中的(长)示例字符串,我确实可以看到一些字符串和 IP 地址。我只是使用上面的代码并添加了一个'cout 【参考方案3】:
fprintf(file, "%h", thing);

类似的东西?

【讨论】:

【参考方案4】:

十六进制是一种显示二进制数据的方式。正如您所说,这不是“原始数据”。如果您拥有的原始数据包含一个字符串,那么当您将其输出到屏幕时,您应该能够看到该字符串(可能还有其他垃圾)。

这是一个在数据块中打印 ASCII 字符的循环。要获得其他任何东西,您必须处理它的格式。

char *binary_data[ BUFFER_SIZE ];
size_t len = BUFFER_SIZE;
len = get_a_packet( data, len ); // or however you get data

for ( char *text_ptr = binary_data; text_ptr != binary_data + len; ++ text_ptr ) 
    if ( * text_ptr <= '~' && * text_ptr >= ' ' )  // if it's ascii
        cerr << * text_ptr; // print it out
    


cerr << endl;

【讨论】:

这不是问题的答案,您尝试查找 ASCII 字符并显示它。为什么在读取字符时分配指针缓冲区?为什么要尝试将此指针数组的地址分配给 char 指针(不应该编译)?为什么要使用错误输出? @Harper:我推测他对二进制数据的性质感到困惑,并且他实际上并没有将文本数据编码为 hex-in-ASCII。要打印某些数据的文本部分,否则您将在其他程序中以十六进制形式查看这些数据,您将跳过非 ASCII 字符。指针缓冲区是错字,应该是纯字符缓冲区。错误输出是因为我把这种事情和调试联系在一起,我使用cerr进行调试,以避免干扰程序的“真实”输出。

以上是关于C++ 十六进制解析的主要内容,如果未能解决你的问题,请参考以下文章

C++,解析二进制注册表文件(regf)

使用 c++ 时从二进制文件中读取 int 不正确

C++笔试强训第六天

经典问题解析四(四十六)

Perl:使用正则表达式将十六进制编码的字符串解析为数组

C++,读取二进制 ifstream 时的奇怪行为