对于我的任务,我需要使用fread / fwrite。我写了
#include <stdio.h>
#include <string.h>
struct rec{
int account;
char name[100];
double balance;
};
int main()
{
struct rec rec1;
int c;
FILE *fptr;
fptr = fopen("clients.txt", "r");
if (fptr == NULL)
printf("File could not be opened, exiting program.\n");
else
{
printf("%-10s%-13s%s\n", "Account", "Name", "Balance");
while (!feof(fptr))
{
//fscanf(fptr, "%d%s%lf", &rec.account, rec.name, &rec.balance);
fread(&rec1, sizeof(rec1),1, fptr);
printf("%d %s %f\n", rec1.account, rec1.name, rec1.balance);
}
fclose(fptr);
}
return 0;
}
clients.txt文件
100 Jones 564.90 200 Rita 54.23 300 Richard -45.00
输出
Account Name Balance 540028977 Jones 564.90 200 Rita 54.23 300 Richard -45.00╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠ ╠╠ü☻§9x°é -92559631349317831000000000000000000000000000000000000000000000.000000 Press any key to continue . . .
我可以用fscanf(已经注释掉)来做到这一点,但是我需要使用fread / fwrite。
我该如何解决这些问题? 非常感谢提前
答案 0 :(得分:6)
正如评论所说,fread
在没有任何解释的情况下读取文件中的字节。文件clients.txt
由50个字符组成,第一行包含16个字符,第二行包含14个字符,第三行包含18个字符,另外还有两个换行符。 (您的clients.txt在第三行之后不包含换行符,您很快就会看到。)换行符在UNIX或Mac OS X计算机上是单字节\n
,但(可能)有两个字节{{ 1)}在Windows机器上 - 因此50或51个字符。以下是十六进制的ASCII字节序列:
\r\n
您的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
语句会将这些字节无需任何解释直接复制到您的fread
数据结构中。该结构以rec1
开头,表示将前四个字节解释为int account;
。正如其中一条评论所指出的那样,您正在一台小端机器(很可能是Intel机器)上运行您的程序,因此最低有效字节是第一个,最重要的字节是第四个。因此,您的int
表示将四个ASCII字符fread
的序列解释为四字节整数"100 "
,它等于十进制0x20303031
。结构的下一个成员是540028977
,这意味着char name[100];
中的下一个100字节数据将是rec1
。但是name
被告知读取fread
字节(4字节帐户,100字节名称,8字节余额)。由于您的文件只有50(或52)个字符,因此sizeof(rec1)=112
只能填写fread
的多个字节。 rec1
的返回值,如果你没有丢弃它,会告诉你读取停止了你请求的字节数。自从你点击EOF后,fread
调用在第一次传递之后突破了循环,一次吞噬了整个文件。
您的所有输出都是由第一个也是唯一一个feof
调用产生的。数字540028977和以下空格由fprintf
和"%d "
参数生成。下一位只是部分确定,你很幸运:rec1.account
说明符和相应的"%s"
参数将打印下一个字符作为ASCII,直到找到rec1.name
字节。因此,输出将以文件的\0
(或50-4
)个剩余字符开头 - 包括两个换行符 - 并且可能永远持续,因为没有52-4
个字节您的文件(或任何文本文件),这意味着在打印文件的最后一个字符后,您看到的是程序启动时自动变量\0
中发生的任何垃圾。 (这种无意识的输出类似于OpenSSL中着名的心脏病。)你很幸运垃圾包含一个rec1
字节后,只有几十个字符。请注意\0
无法知道printf
被声明为只有一个100字节的数组 - 它只有指向rec1.name
的开头 - 你有责任保证name
包含终止rec1.name
字节,而您从未这样做过。
我们可以多说一点。数字\0
(-9.2559631349317831e61
格式中非常丑陋)是"%f"
的值。 IEEE 754计算机(如英特尔和所有现代计算机)上rec1.balance
值的8个字节为十六进制double
。 64个奇特的0xcccccccccccccccc
符号出现在与╠
对应的"%s"
输出中,而100个中仅剩下100-46 = 54个字符,因此您的rec1.name
输出已包含在"%s"
结束时运行,并将rec1.name
包含在讨价还价中,我们了解到您的终端程序将非ASCII字符rec1.balance
解释为0xcc
。有很多方法可以解释大于127的字节(0x7f);例如,在latin-1中它可能是╠
。图形字符Ì
表示古代MS-DOS字符集中的0xcc(204)字节,Windows代码页437.您不仅在Intel机器上运行,它还是Windows机器(当然最有可能的开始)。
这回答了你的前两个问题。我不确定我理解你的第三个问题。 &#34;缺点&#34;我希望很明显。
至于如何修复它,使用╠
读取和解释文本文件没有相当简单的方法。为此,您需要复制fread
libc
函数中的大部分代码。唯一合理的方法是首先使用fscanf
创建二进制文件;然后fwrite
会自然地将其读回来。因此必须有两个程序 - 一个用于编写二进制fread
文件,另一个用于读取它。当然,这并不能解决第一个程序的数据首先来自何处的问题。它可能来自使用clients.bin
阅读clients.txt
。或者它可以包含在fscanf
程序的源代码中,例如通过像这样初始化fwrite
数组:
struct rec
或者它可能来自于阅读MySQL数据库,或者......它不太可能来自一个二进制文件(易于)可读struct rec recs[] = {{100, "Jones", 564.90},
{200, "Rita", 54.23},
{300, "Richard", -45.00}};
。