从文件中读取十六进制值并在C中显示它们

时间:2017-09-27 08:33:42

标签: c hex fgets

我有一个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

为什么输出与文件内容不对应?

2 个答案:

答案 0 :(得分:2)

保罗R。: 在典型的ASCII环境中,0a是换行符(LF\n),它会终止您的fgets调用,因此您无法读取剩余的字节。
使用fread而不是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个字符保留了第一次定义数组时包含的未初始化的垃圾值。 (在这种情况下,它们看起来像80d273b7。)

但是在这种情况下,你真正想要做的是,读取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