fread()c中的结构

时间:2015-11-06 14:03:47

标签: c struct io fread

对于我的任务,我需要使用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。

  1. 为什么Jone的帐号会以大量数字开头?
  2. 为什么之后会有垃圾?难道不应该停止这个吗?
  3. 使用这种方法有什么缺点吗?还是fscanf方法?
  4. 我该如何解决这些问题? 非常感谢提前

1 个答案:

答案 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中它可能是。图形字符&Igrave;表示古代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}};