fread(cur,2,1,fin)
我确信当我得到答案时我会感到愚蠢,但是发生了什么?
cur是指向code_cur的指针,短(2个字节),fin是为二进制读取打开的流。
如果我的文件是00101000 01000000
我最终得到的是
code_cur = 01000000 00101000
为什么?我还没有进行任何比赛,因为问题实际上归结为(至少对我而言)意外行为。
如果这是规范,我怎样才能获得所需的效果?
P.S。
我应该补充一点,为了'查看'字节,我打印它们的整数值。
printf("%d\n",code_cur)
我尝试了几次,看起来很可靠。
答案 0 :(得分:3)
正如其他人指出的那样,您需要在endianness上了解更多信息。
您不知道,但您的文件(幸运的是)在网络字节顺序(Big Endian)中。您的机器是小端,因此需要进行校正。需要与否,始终建议进行此更正,因为这将保证您的程序在任何地方运行。
做一些类似的事情:
aaItems = new ArrayAdapter(this, android.R.layout.simple_list_item_1,
new ArrayList<String>(arrMonths));
{
uint16_t tmp;
if (1 == fread(&tmp, 2, 1, fin)) { /* Check fread finished well */
code_cur = ntohs(tmp);
} else {
/* Treat error however you see fit */
perror("Error reading file");
exit(EXIT_FAILURE); // requires #include <stdlib.h>
}
}
会将您的价值从文件订单转换为您机器的订单,无论是大端还是小端。
答案 1 :(得分:1)
这就是htonl和htons(和朋友)存在的原因。它们不是C标准库的一部分,但它们几乎可以在每个进行网络连接的平台上使用。
“htonl”的意思是“主机到网络,长”; “htons”表示“主机到网络,短”。在此上下文中,“long”表示32位,“short”表示16位(即使平台将“long”声明为64位)。基本上,每当你从“网络”(或者在你的情况下,你正在阅读的流中)读取某些内容时,你会通过“ntoh *”传递它。当你写出来时,你将它传递给“hton *”
您可以以任何您想要的方式置换这些函数名称,除了愚蠢的(不,没有ntons,也没有stonl)
答案 2 :(得分:0)
正如其他人指出的那样,这是一个流行病问题。
最高有效字节在文件和计算机上有所不同。您的文件具有高字节序(MSB在前),而计算机是低字节序(MSB在后或LSB在前)。
要了解发生了什么,让我们创建一个包含一些二进制数据的文件:
uint8_t buffer[2] = {0x28, 0x40}; // hexadecimal for 00101000 01000000
FILE * fp = fopen("file.bin", "wb"); // opens or creates file as binary
fwrite(buffer, 1, 2, fp); // write two bytes to file
fclose(fp);
file.bin
已创建并保存为二进制值00101000 01000000,让我们看一下:
uint8_t buffer[2] = {0, 0};
FILE * fp = fopen("file.bin", "rb");
fread(buffer, 1, 2, fp); // read two bytes from file
fclose(fp);
printf("0x%02x, 0x%02x\n", buffer[0], buffer[1]);
// The above prints 0x28, 0x40, as expected and in the order we wrote previously
所以一切正常,因为我们正在逐字节读取字节,而字节没有字节序(从技术上讲,它们总是字节序,它们始终是最重要的位 )机器,但您可能会认为它们并没有简化理解)。
无论如何,正如您所注意到的,当您尝试直接阅读短片时,会发生以下情况:
FILE * fp_alt = fopen("file.bin", "rb");
short incorrect_short = 0;
fread(&incorrect_short, 1, 2, fp_alt);
fclose(fp_alt);
printf("Read short as machine endianess: %hu\n", incorrect_short);
printf("In hex, that is 0x%04x\n", incorrect_short);
// We get the incorrect decimal of 16424 and hex of 0x4028!
// The machine inverted our short because of the way the endianess works internally
最糟糕的是,如果您使用的是big-endian机器,则上述结果将不会返回错误的数字,从而使您不知道您的代码是特定于endian的并且不能在处理器之间移植!
使用ntohs
中的arpa/inet.h
来转换字节序是很不错的,但是我觉得很奇怪,因为它是用于网络通信的完整(非标准)库,用于解决阅读时遇到的问题文件,它通过从文件中错误地读取它,然后“转换”错误的值而不是正确地读取它来解决该问题。
在高级语言中,我们经常看到用于从文件读取字节序而不是转换值的函数,因为我们(通常)知道文件结构的方式及其字节序,只需查看Javascript Buffer的readInt16BE
{{3} },指向重点并易于使用。
出于这种简单性的考虑,我创建了一个函数,该函数读取下面的16位整数(但是如果需要,可以很容易地将其更改为8、32或64位):
#include <stdint.h> // necessary for specific int types
// Advances and reads a single signed 16-bit integer from the file descriptor as Big Endian
// Writes the value to 'result' pointer
// Returns 1 if succeeds or 0 if it fails
int8_t freadInt16BE(int16_t * result, FILE * f) {
uint8_t buffer[sizeof(int16_t)];
if (!result || !f || sizeof(int16_t) != fread((void *) buffer, 1, sizeof(int16_t), f))
return 0;
*result = buffer[0] << 8 + buffer[1];
return 1;
}
用法很简单(为简便起见,省略了错误处理):
FILE * fp = fopen("file.bin", "rb"); // Open file as binary
short code_cur = 0;
freadInt16BE(&code_cur, fp);
fclose(fp);
printf("Read Big-Endian (MSB first) short: %hu\n", code_cur);
printf("In hex, that is 0x%04x\n", code_cur);
// The above code prints 0x2840 correctly (decimal: 10304)
如果以下文件中的一个文件不存在,无法打开或不包含要在当前位置读取的2个字节,则该函数将失败(返回0)。
作为奖励,如果您碰巧找到的文件是little-endian,则可以使用以下功能:
// Advances and reads a single signed 16-bit integer from the file descriptor as Little Endian
// Writes the value to 'result' pointer
// Returns 1 if succeeds or 0 if it fails
int8_t freadInt16LE(int16_t * result, FILE * f) {
uint8_t buffer[sizeof(int16_t)];
if (!result || !f || sizeof(int16_t) != fread((void *) buffer, 1, sizeof(int16_t), f))
return 0;
*result = buffer[1] << 8 + buffer[0];
return 1;
}