正如评论所说,fread
读取文件中的字节而不进行任何解释。文件clients.txt
由 50 个字符组成,第一行 16 个,第二行 14 个,第三行 18 个,再加上两个换行符。 (您的clients.txt在第三行之后不包含换行符,您很快就会看到。)换行符是一个字节\n
在 UNIX 或 Mac OS X 机器上,但是(可能)两个字节\r\n
在 Windows 计算机上 - 因此为 50 或 51 个字符。以下是十六进制的 ASCII 字节序列:
3130 3020 4a6f 6e65 7320 3536 342e 3930 100 Jones 564.90
0a32 3030 2052 6974 6120 3534 2e32 330a \n200 Rita 54.23\n
3330 3020 5269 6368 6172 6420 2d34 352e 300 Richard -45.
3030 00
Your fread
语句将这些字节直接复制到您的文件中,无需任何解释rec1
数据结构。该结构开始于int account;
,它表示将前四个字节解释为int
。正如其中一条评论指出的,您正在小端机器(很可能是英特尔机器)上运行程序,因此最低有效字节是第一个,最高有效字节是第四个。因此,你的fread
表示解释四个 ASCII 字符的序列"100 "
作为四字节整数0x20303031
,以十进制表示,等于540028977
。你的结构的下一个成员是char name[100];
,这意味着接下来的 100 个字节的数据rec1
将是name
。但是fread
被告知要读书sizeof(rec1)=112
字节(4 字节帐户,100 字节名称,8 字节余额)。由于您的文件只有 50(或 52)个字符,fread
将只能填充那么多字节rec1
。返回值fread
,如果您没有丢弃它,则会告诉您读取未达到您请求的字节数而停止。自从你击中 EOF 以来,feof
调用在第一次传递后中断循环,一次性消耗了整个文件。
您的所有输出都是由第一个也是唯一一个调用生成的fprintf
。编号540028977及以下空间由"%d "
和rec1.account
争论。接下来的部分只是部分确定的,你很幸运:"%s"
说明符和相应的rec1.name
参数将以 ASCII 形式打印接下来的字符,直到\0
字节被发现。因此,输出将以50-4
(or 52-4
)文件的剩余字符(包括两个换行符)并可能永远继续,因为没有\0
文件(或任何文本文件)中的字节,这意味着在打印文件的最后一个字符后,您看到的是自动变量中发生的任何垃圾rec1
当你的程序开始时。 (这种无意的输出类似于 OpenSSL 中著名的 heartbleed bug。)你很幸运,垃圾包括\0
仅几十个字符之后的字节。注意printf
无从得知rec1.name
被声明为只是一个 100 字节数组——它只得到了指向开头的指针name
——你有责任保证这一点rec1.name
包含终止\0
字节,而你从来没有这样做过。
我们可以多说一点。号码-9.2559631349317831e61
(这在"%f"
格式)的值是rec1.balance
。 8 个字节double
IEEE 754 机器(如英特尔和所有现代计算机)上的值以十六进制表示0xcccccccccccccccc
。六十四奇特╠
符号出现在"%s"
输出对应于rec1.name
,而 100 个字符中只剩下 100-46 = 54 个字符,所以你的"%s"
输出已结束rec1.name
,并包括rec1.balance
讨价还价,我们了解到您的终端程序解释了非 ASCII 字符0xcc
as ╠
。有很多方法可以解释大于 127 (0x7f) 的字节;在 latin-1 中它会是Ì
例如。图形字符╠
是古代 MS-DOS 字符集中 0xcc (204) 字节的表示,Windows 代码页 437。您不仅在 Intel 机器上运行,它还是 Windows 机器(当然最有可能的可能性是开始) 。
这回答了你的前两个问题。我不确定我是否理解你的第三个问题。我希望“缺点”是显而易见的。
至于如何修复它,没有相当简单的方法来读取和解释文本文件fread
。为此,您需要复制其中的大部分代码libc
fscanf
功能。唯一明智的方法是首先使用fwrite
创建一个二进制文件;然后fread
会很自然地读回来。所以必须有两个程序——一个用于编写二进制文件clients.bin
文件,然后再读回它。当然,这并不能解决第一个程序的数据首先应该来自哪里的问题。它可能来自阅读clients.txt
using fscanf
。或者它可以包含在源代码中fwrite
程序,例如通过初始化数组struct rec
像这样:
struct rec recs[] = {{100, "Jones", 564.90},
{200, "Rita", 54.23},
{300, "Richard", -45.00}};
或者它可能来自读取 MySQL 数据库,或者......它不太可能起源于一个二进制文件(很容易)可读fread
.