在 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<<(stringstream&, 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 <= c
,而不是'0' <= c
。有区别!
@Nawaz: 反过来——应该是'9'
而不是9
:-) 谢谢,我修好了。这里的假设是 OP 正在读取 text ,它以十六进制表示数字。我可能对这个假设有误,但这就是我理解这个问题的方式。
哦,是的。我只是感到困惑:P以上是关于在 C/C++ 中处理十六进制值的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Python 中将单个字符转换为其十六进制 ASCII 值?