对于我的任务,我需要使用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;
}
Run Code Online (Sandbox Code Playgroud)
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.
我该如何解决这些问题?提前谢谢了
正如评论所说,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
Run Code Online (Sandbox Code Playgroud)
您的fread语句将这些字节无需任何解释直接复制到您的rec1数据结构中.该结构以#开头int account;,将前四个字节解释为int.正如其中一条评论所指出的那样,您正在一台小端机器(很可能是Intel机器)上运行您的程序,因此最低有效字节是第一个,最重要的字节是第四个.因此,您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(或52-4)剩余字符开头 - 包括两个换行符 - 并可能永远持续,因为\0文件(或任何文本文件)中没有字节,这意味着打印后你文件的最后一个字符,你看到的是rec1程序启动时自动变量中发生的垃圾.(这种无意识的输出类似于OpenSSL中着名的心脏病.)你很幸运垃圾只包含了\0几十个字符之后的一个字节.注意,printf无法知道rec1.name声明只是一个100字节的数组 - 它只有指向开头的指针name- 你有责任保证rec1.name包含一个终止\0字节,你从来没有这样做过.
我们可以多说一点.数字-9.2559631349317831e61("%f"格式相当丑陋)是值rec1.balance.doubleIEEE 754计算机(如英特尔和所有现代计算机)上的该值的8个字节为十六进制0xcccccccccccccccc.64个特殊?符号出现在"%s"输出对应的中rec1.name,而100个中只剩下100-46 = 54个字符,所以你的"%s"输出已经结束了rec1.name,并包含rec1.balance在讨价还价中,我们得知您的终端程序被解释非ASCII字符0xcc为?.有很多方法可以解释大于127的字节(0x7f); 例如,拉丁语1就是这样Ì.图形字符?是古代MS-DOS字符集(Windows代码页437)中0xcc(204)字节的表示.您不仅在Intel机器上运行,它还是Windows机器(当然,很可能是首先).
这回答了你的前两个问题.我不确定我理解你的第三个问题.我希望显而易见的"弊端".
至于如何解决它,没有相当简单的方法来使用读取和解释文本文件fread.为此,您需要复制libc fscanf函数中的大部分代码.唯一明智的方法是首先使用fwrite创建二进制文件; 然后fread会自然地阅读它.因此必须有两个程序 - 一个用于编写二进制clients.bin文件,另一个用于读取它.当然,这并不能解决第一个程序的数据首先来自何处的问题.它可能来自阅读clients.txt使用fscanf.或者它可以包含在程序的源代码中fwrite,例如通过初始化这样的数组struct rec:
struct rec recs[] = {{100, "Jones", 564.90},
{200, "Rita", 54.23},
{300, "Richard", -45.00}};
Run Code Online (Sandbox Code Playgroud)
或者它可能来自于读取MySQL数据库,或者...它不太可能来自一个二进制文件(易于)可读fread.