如何将 "hexadecimal" 个数字 [C/Linux] 的 char[4] 转换为整数
How to convert to integer a char[4] of "hexadecimal" numbers [C/Linux]
所以我正在 Linux 中处理系统调用。我正在使用 "lseek" 浏览文件并使用 "read" 阅读。我还使用 Midnight Commander 以十六进制查看文件。接下来我要读取的 4 个字节是 little-endian ,看起来像这样:“2A 00 00 00”。但当然,字节可以是“2A 5F B3 00”之类的东西。我必须将这些字节转换为整数。我该如何处理?我最初的想法是将它们读入 4 个字符的向量,然后从那里构建我的整数,但我不知道如何。有什么想法吗?
让我举一个我尝试过的例子。我在文件“44 00”中有以下字节。我必须将其转换为值 68 (4 + 4*16):
char value[2];
read(fd, value, 2);
int i = (value[0] << 8) | value[1];
变量i是17480 insead of 68
更新:Nvm。我解决了。我在移动时混合了索引。它应该是 value[1] << 8 ... |值[0]
假设您指向缓冲区:
unsigned char *p = &buf[20];
并且您想将接下来的 4 个字节视为一个整数并将它们分配给您的整数,那么您可以 cast it:
int i;
i = *(int *)p;
你刚才说 p 现在是一个指向 int 的指针,你取消引用那个指针并将它赋值给 i。
但是,这取决于您平台的字节顺序。如果您的平台有不同的字节顺序,您可能首先必须将字节反向复制到一个小缓冲区,然后使用此技术。例如:
unsigned char ibuf[4];
for (i=3; i>=0; i--) ibuf[i]= *p++;
i = *(int *)ibuf;
编辑
Andrew Henle 和 Bodo 的建议和意见可以给出:
unsigned char *p = &buf[20];
int i, j;
unsigned char *pi= &(unsigned char)i;
for (j=3; j>=0; j--) *pi++= *p++;
// and the other endian:
int i, j;
unsigned char *pi= (&(unsigned char)i)+3;
for (j=3; j>=0; j--) *pi--= *p++;
一般注意事项
这个问题似乎有几个部分 -- 至少是如何读取数据、使用什么数据类型来保存中间结果以及如何执行转换。如果您确实假设文件上的表示形式由 little-endian 顺序的 32 位整数的字节组成,所有位都有效,那么我可能不会使用 char[]
作为中间值,而是一个 uint32_t
或一个 int32_t
。如果您知道或假设数据的字节序与机器的本机字节序相同,那么您不需要任何其他字节序。
确定本机字节顺序
如果您需要计算主机的本机字节顺序,则可以这样做:
static const uint32_t test = 1;
_Bool host_is_little_endian = *(char *)&test;
这样做是值得的,因为很可能根本不需要进行任何转换。
正在读取数据
我会将数据读入 uint32_t
(或者可能是 int32_t
),而不是 char
数组。可能我会把它读入 uint8_t
.
的数组
uint32_t data;
int num_read = fread(&data, 4, 1, my_file);
if (num_read != 1) { /* ... handle error ... */ }
正在转换数据
了解文件上的表示是否与主机的字节顺序相匹配是值得的,因为如果匹配,则您不需要进行任何转换(也就是说,在这种情况下,您此时已完成)。但是,如果 do 需要交换字节顺序,则可以使用 ntohl()
或 htonl()
:
if (!host_is_little_endian) {
data = ntohl(data);
}
(假设小端和大端是您唯一需要关注的主机字节顺序。历史上还有其他的,这就是字节重新排序函数成对出现的原因,但您极不可能见到其他人之一。)
有符号整数
如果您需要有符号整数而不是无符号整数,那么您也可以这样做,但要使用联合:
union {
uint32_t unsigned;
int32_t signed;
} data;
以上都是用data.unsigned
代替普通的data
,最后从data.signed
中读出签名后的结果。
所以我正在 Linux 中处理系统调用。我正在使用 "lseek" 浏览文件并使用 "read" 阅读。我还使用 Midnight Commander 以十六进制查看文件。接下来我要读取的 4 个字节是 little-endian ,看起来像这样:“2A 00 00 00”。但当然,字节可以是“2A 5F B3 00”之类的东西。我必须将这些字节转换为整数。我该如何处理?我最初的想法是将它们读入 4 个字符的向量,然后从那里构建我的整数,但我不知道如何。有什么想法吗?
让我举一个我尝试过的例子。我在文件“44 00”中有以下字节。我必须将其转换为值 68 (4 + 4*16):
char value[2];
read(fd, value, 2);
int i = (value[0] << 8) | value[1];
变量i是17480 insead of 68
更新:Nvm。我解决了。我在移动时混合了索引。它应该是 value[1] << 8 ... |值[0]
假设您指向缓冲区:
unsigned char *p = &buf[20];
并且您想将接下来的 4 个字节视为一个整数并将它们分配给您的整数,那么您可以 cast it:
int i;
i = *(int *)p;
你刚才说 p 现在是一个指向 int 的指针,你取消引用那个指针并将它赋值给 i。
但是,这取决于您平台的字节顺序。如果您的平台有不同的字节顺序,您可能首先必须将字节反向复制到一个小缓冲区,然后使用此技术。例如:
unsigned char ibuf[4];
for (i=3; i>=0; i--) ibuf[i]= *p++;
i = *(int *)ibuf;
编辑
Andrew Henle 和 Bodo 的建议和意见可以给出:
unsigned char *p = &buf[20];
int i, j;
unsigned char *pi= &(unsigned char)i;
for (j=3; j>=0; j--) *pi++= *p++;
// and the other endian:
int i, j;
unsigned char *pi= (&(unsigned char)i)+3;
for (j=3; j>=0; j--) *pi--= *p++;
一般注意事项
这个问题似乎有几个部分 -- 至少是如何读取数据、使用什么数据类型来保存中间结果以及如何执行转换。如果您确实假设文件上的表示形式由 little-endian 顺序的 32 位整数的字节组成,所有位都有效,那么我可能不会使用 char[]
作为中间值,而是一个 uint32_t
或一个 int32_t
。如果您知道或假设数据的字节序与机器的本机字节序相同,那么您不需要任何其他字节序。
确定本机字节顺序
如果您需要计算主机的本机字节顺序,则可以这样做:
static const uint32_t test = 1;
_Bool host_is_little_endian = *(char *)&test;
这样做是值得的,因为很可能根本不需要进行任何转换。
正在读取数据
我会将数据读入 uint32_t
(或者可能是 int32_t
),而不是 char
数组。可能我会把它读入 uint8_t
.
uint32_t data;
int num_read = fread(&data, 4, 1, my_file);
if (num_read != 1) { /* ... handle error ... */ }
正在转换数据
了解文件上的表示是否与主机的字节顺序相匹配是值得的,因为如果匹配,则您不需要进行任何转换(也就是说,在这种情况下,您此时已完成)。但是,如果 do 需要交换字节顺序,则可以使用 ntohl()
或 htonl()
:
if (!host_is_little_endian) {
data = ntohl(data);
}
(假设小端和大端是您唯一需要关注的主机字节顺序。历史上还有其他的,这就是字节重新排序函数成对出现的原因,但您极不可能见到其他人之一。)
有符号整数
如果您需要有符号整数而不是无符号整数,那么您也可以这样做,但要使用联合:
union {
uint32_t unsigned;
int32_t signed;
} data;
以上都是用data.unsigned
代替普通的data
,最后从data.signed
中读出签名后的结果。