我有一个key.hex文件,其中包含:
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
作为十六进制值。我尝试阅读它们并立即打印出来:
unsigned char key[16];
FILE *secretkey;
secretkey = fopen("key.hex", "r+");
fgets(key, 16, secretkey);
int j = 0;
for(j = 0; j < sizeof(key) ; j++) {
printf("%02x ", key[j]);
}
然而,当我得到以下输出时:
00 01 02 03 04 05 06 07 08 09 0a 00 80 d2 73 b7
为什么输出与文件内容不对应?
答案 0 :(得分:2)
0a
是换行符(LF
或\n
),它会终止您的fgets调用,因此您无法读取剩余的字节。 E.g。
(来自BLUEPIXY评论中链接的代码中最相关的行,这是一个完整的演示者):
secretkey = fopen("key.hex", "r+b");
fread(key, 1, sizeof key, secretkey);
(这是两个评论的答案,每个评论都是一个不错的答案。如果两个作者中的任何一个做出自己的答案并要求我,我提议删除。)
答案 1 :(得分:1)
你说该文件包含这些十六进制值:
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
因此该文件恰好包含16个字节:第一个包含值0x00
,第二个包含值0x01
等。
这就是所谓的二进制文件。它不是一个文本文件。该文件中几乎没有普通的可打印字符。如果我们要将该文件打印到屏幕上,我们会看到奇怪的字形和垃圾,或者什么都没有。
但fgets
函数用于从文本文件中读取一行 text 。具体来说,它读取字符直到并包括行尾指示。在Unix / Linux系统上,行尾指示是换行符。在ASCII中,换行符包含字符代码0x0a
。此外,由于fgets
函数旨在使用文本,因此它通过在末尾附加空字节(0x00
或'\0'
)将其读取的文本转换为C字符串。输出缓冲区。
所以发生的事情是fgets
只读取文件中的前11个字符,包括0A
,并将这些字符和终止空字符写入key
阵列。所以它只填充了阵列的前12个单元格。最后4个字符保留了第一次定义数组时包含的未初始化的垃圾值。 (在这种情况下,它们看起来像80
,d2
,73
和b7
。)
但是在这种情况下,你真正想要做的是,读取16个原始字节,而不假设它们是文本,而不是寻找新行,而不使用null终止结果。这是一个非常不同的要求,但它发现了另一个标准库函数,它完全符合您的要求:fread
。
所以重写你的代码:
FILE *secretkey;
secretkey = fopen("key.hex", "rb");
if(secretkey == NULL) {
fprintf(stderr, "can't open %s\n", "key.hex");
exit(1);
}
int r = fread(key, 1, 16, secretkey);
if(r != 16) {
fprintf(stderr, "error reading key: read %d chars, expected %d\n", r, 16);
exit(1);
}
int j;
for(j = 0; j < sizeof(key) ; j++) {
printf("%02x ", key[j]);
}
您注意到我还添加了代码来检查fread
是否确实按预期读取了16个字节。我还添加了代码来检查fopen
调用是否成功打开了&#34; key.hex&#34;文件。此外,我已更改fopen
调用以二进制模式("rb"
)打开文件,并删除您可能不想要的+
修饰符。< / p>
作为旁注,如果您想要做更好的错误处理,当打开文件失败时,打印更具描述性的错误消息总是一个好主意,指示为什么文件无法打开:
if(secretkey == NULL) {
fprintf(stderr, "can't open %s: %s\n", "key.hex", strerror(errno));
exit(1);
}
为此,您还必须为<string.h>
函数添加标题文件strerror()
,并为<errno.h>
值包含errno
。