在 C/C++ 中处理十六进制值

Posted

技术标签:

【中文标题】在 C/C++ 中处理十六进制值【英文标题】:Dealing with hex values in C/C++ 【发布时间】:2011-08-26 15:07:16 【问题描述】:

我使用 winsock 从网络上的另一台计算机接收值。它是一个 TCP 套接字,消息的前 4 个字节带有它的大小。消息的其余部分由服务器使用 protobuf(来自 google 的协议缓冲区)格式化。

我认为,问题在于服务器发送的值似乎是作为 char 发送的十六进制值(即 0x10 仅收到 10 个)。要接收这些值,我会这样做:

bytesreceived = recv(sock, buffer, msg_size, 0);
for (int i=0;i<bytesreceived;i++) 

    data_s << hex << buffer[i];

其中 data_s 是一个字符串流。他们我可以使用 protobuf 中的 ParseFromIstream(&data_s) 方法并恢复我想要的信息。

我遇到的问题是它非常长(我使用 QSock 获得了另一个实现,但我不能用于我的项目,但它更快,所以服务器端没有问题)。

我尝试了许多从互联网上和任何地方获取的东西(使用字节数组、字符串),但没有任何效果。

我还有其他选择吗?

感谢您的时间和 cmets ;)

【问题讨论】:

澄清一下,您想将[0-9A-F] 范围内的一长串 ASCII 字符转换成……什么?成字节流?大端 4 字节整数流? 这个问题和C有关系吗?它看起来像 C++。 流、数组或字符串,因为 protobuf 可以处理所有这些事情。我认为目标是获得 big-endian 4 字节整数,但所有这一切在我的脑海中都不是很清楚.. @Fred Larson:对。我已经重新标记了它。 @Fred :我的代码是 C++,因为我使用流,但如果我能得到涉及 C 而不是 C++ 的帮助,我会非常高兴 :) 【参考方案1】:

不确定这是否有用,但我之前使用过类似的协议(前 4 个字节包含一个长度为 int 的字节,其余字节使用 protobuf 编码)并解码它我做了类似的事情(可能由于附加到字符串,这不是最有效的解决方案):

// Once I've got the first 4 bytes, cast it to an int:
int msgLen = ntohl(*reinterpret_cast<const int*>(buffer));

// Check I've got enough bytes for the message, if I have then 
// just parse the buffer directly
MyProtobufObj obj;
if( bytesreceived >= msgLen+4 )

  obj.ParseFromArray(buffer+4,msgLen);

else

  // just keep appending buffer to an STL string until I have 
  // msgLen+4 bytes and then do
  // obj.ParseFromString(myStlString)

【讨论】:

是的!非常感谢。我认为我对十六进制和流的问题太过分了,无法看到那个简单的解决方案,只使用字符串中的 append() 方法。【参考方案2】:

我不会使用流运算符。它们用于格式化数据,这不是您想要的。

您可以将接收到的值保存在具有 char 类型(字节向量)的 std::vector 中。那基本上只是一个动态数组。如果你想继续使用字符串流,你可以使用 stringstream::write 函数,它接受一个缓冲区和一个长度。您应该拥有从调用 recv 时收到的缓冲区和字节数。

如果你想使用vector方法,你可以使用std::copy来简化。

#include <algorithm>
#include <iterator>
#include <vector>

char buf[256];
std::vector<char> bytes;
size_t n = recv(sock, buf, 256, 0);
std::copy(buf, buf + n, std::back_inserter(bytes));

【讨论】:

谢谢。我已经想过使用向量,但是 protobuf 无法解析它们。【参考方案3】:

你的问题有点模棱两可。让我们按照你的例子。您收到 10 作为字符,并且您希望将其检索为十六进制数字。

假设recv会给你这个字符串,你可以这样做。

首先让它空终止:

bytesreceived[msg_size] = '\0';

那么你可以很容易地使用标准的 *scanf 函数从这个缓冲区中读取值:

int hexValue;
sscanf(bytesreceived, "%x", &hexValue);

给你!

编辑:如果您以相反的顺序收到数字(所以 01 对应于 10),最好的办法可能是手动转换它:

int hexValue = 0;
int positionValue = 1;
for (int i = 0; i < msg_size; ++i)

    int digit = 0;
    if (bytesreceived[i] >= '0' && bytesreceived[i] <= '9')
        digit = bytesreceived[i]-'0';
    else if (bytesreceived[i] >= 'a' && bytesreceived[i] <= 'f')
        digit = bytesreceived[i]-'a';
    else if (bytesreceived[i] >= 'A' && bytesreceived[i] <= 'F')
        digit = bytesreceived[i]-'A';
    else // Some kind of error!
        return error;
    hexValue += digit*positionValue;
    positionValue *= 16;

不过,这只是一个明显的例子。实际上,您可以通过位移而不是乘法来实现。

【讨论】:

【参考方案4】:

buffer 是什么数据类型?

整个事情看起来像是一个很棒的大空操作,因为operator&lt;&lt;(stringstream&amp;, char) 忽略了基本说明符。 hex 说明符仅影响 非字符 整数类型的格式。当然,您不想将文本数据交给 protobuf。

只需将buffer 指针交给protobuf,就完成了。

【讨论】:

这是我尝试的第一件事(实际上并不完全正确,因为消息很大,我首先逐块接收数据并将所有内容放入数组中)。而且,由于我不明白的原因,这不起作用。 @Ben:我重复我的问题:“buffer 是什么数据类型?” 它是从套接字接收的字节流。其实你是对的,我真的走错了方向。尽管如此,将缓冲区指针交给 protobuf 还是行不通的……尽管它应该【参考方案5】:

好的,深入了解一下:假设您的入口流是"71F4E81DA...",并且您想将其转换为字节流 0x71, 0xF4, 0xE8, ...。然后我们可以从字符文字中组装字节,如下所示:

char * p = getCurrentPointer();

while (chars_left() >= 2)

  unsigned char b;
  b  = get_byte_value(*p++) << 8;
  b += get_byte_value(*p++);

  output_stream.insert(b);

这里我们使用一个小辅助函数:

unsigned char get_byte_value(char c)

  if ('0' <= c && c <= '9') return c - '0';
  if ('A' <= c && c <= 'F') return 10 + c - 'A';
  if ('a' <= c && c <= 'f') return 10 + c - 'a';

  return 0;  // error

【讨论】:

应该是0 &lt;= c,而不是'0' &lt;= c。有区别! @Nawaz: 反过来——应该是'9' 而不是9 :-) 谢谢,我修好了。这里的假设是 OP 正在读取 text ,它以十六进制表示数字。我可能对这个假设有误,但这就是我理解这个问题的方式。 哦,是的。我只是感到困惑:P

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

C语言中ASCII码值是啥意思

如何在python中将单个字符转换为十六进制ascii值

如何在 Python 中将单个字符转换为其十六进制 ASCII 值?

在 C/C++ 中高效地在十六进制、二进制和十进制之间进行转换

读取 uart 十六进制值会忽略某些字节/字符

C语言中*p=0表示?