fread() c 中的结构

2024-02-03

对于我的作业,我需要使用 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;
}

客户.txt 文件



100 Jones 564.90
200 Rita 54.23
300 Richard -45.00
  

output



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. 为什么琼斯的帐户以大量数字开头?
  2. 为什么之后有垃圾? feof 不应该阻止这一切吗?
  3. 使用这种方法有什么缺点吗?或者fscanf方法?

我该如何解决这些问题? 提前谢谢了


正如评论所说,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 个字节doubleIEEE 754 机器(如英特尔和所有现代计算机)上的值以十六进制表示0xcccccccccccccccc。六十四奇特符号出现在"%s"输出对应于rec1.name,而 100 个字符中只剩下 100-46 = 54 个字符,所以你的"%s"输出已结束rec1.name,并包括rec1.balance讨价还价,我们了解到您的终端程序解释了非 ASCII 字符0xcc as 。有很多方法可以解释大于 127 (0x7f) 的字节;在 latin-1 中它会是&Igrave;例如。图形字符是古代 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.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

fread() c 中的结构 的相关文章

随机推荐