c - fread() c 中的一个结构体

标签 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;
}

客户端.txt文件

100 琼斯 564.90
200 丽塔 54.23
300 理查德 -45.00

输出

账户名余额
540028977 琼斯 564.90
200 丽塔 54.23
300理查德-45.00╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠ ╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠
╠╠ü☻§9x°é -9255963134931783100000000000000000000000000000000000000000000.000000

按任意键继续 。 . .

我可以用 fscanf(我已经注释掉)来做到这一点,但我需要使用 fread/fwrite。
  • 为什么它以琼斯帐户的大量数字开头?
  • 为什么后面有垃圾? feof 不应该阻止吗?
  • 使用这种方法有什么缺点吗?或 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
    

    您的 fread语句将这些字节直接复制到您的 rec1 中,无需任何解释。数据结构。该结构以 int account; 开头,表示将前四个字节解释为 int .正如其中一条评论所指出的,您正在小端机器(最有可能是 Intel 机器)上运行您的程序,因此最低有效字节是第一个,最高有效字节是第四个字节。因此,您的 fread据说可以解释四个 ASCII 字符的序列 "100 "作为四字节整数 0x20303031 , 以十进制表示,等于 540028977 .结构体的下一个成员是 char name[100]; ,这意味着 rec1 中接下来的 100 个字节的数据将是 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 中著名的 heartbleed 错误。)你很幸运垃圾中包含了 \0字节后只有几十个字符。请注意 printf没办法知道rec1.name被声明为只有一个 100 字节的数组——它只得到了指向 name 开头的指针-- 您有责任保证 rec1.name包含终止 \0字节,而你从未这样做过。

    我们可以多说一点。号码-9.2559631349317831e61 (在 "%f" 格式中很难看)是 rec1.balance 的值.该 double 的 8 个字节IEEE 754 机器(如您的 Intel 和所有现代计算机)上的值以十六进制表示 0xcccccccccccccccc .六十四奇葩符号出现在 "%s"输出对应于 rec1.name ,而 100 个字符只剩下 100-46 = 54 个字符,所以您的 "%s"输出已结束 rec1.name ,并包括 rec1.balance讨价还价,我们了解到您的终端程序解释了非 ASCII 字符 0xcc .有很多方法可以解释大于 127 (0x7f) 的字节;在 latin-1 中应该是 &Igrave;例如。图形字符是古代 MS-DOS 字符集中的 0xcc (204) 字节的表示,Windows 代码页 437。您不仅在 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}};
    

    或者它可能来自读取 MySQL 数据库,或者......它不太可能来自一个二进制文件(很容易)用 fread 读取。 .

    关于c - fread() c 中的一个结构体,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33568577/

    相关文章:

    c - 在 C 代码中通过引用将结构传递给函数(编译错误)

    c - 无法理解 sizeof

    python - 在 Python 中实现正则表达式以替换文本文件中每次出现的 "meshname = x"

    java - 如何在 JNI 中处理类结构体 jobject

    c - 在 C 中使用 NULL 指针就像在 Java 中使用 null 一样?

    c - 为什么我的函数错误地返回二维数组的一部分?

    file - 逐行读取大文件

    c - 在 C 中绘制表格——就像 Linux 手册页上的表格

    c - 读取C中的结构体?

    c++ - SetFileCompletionNotificationModes 似乎无法正常工作