fread()c中的结构 [英] fread() a struct in c

查看:63
本文介绍了fread()c中的结构的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

对于我的作业,我需要使用fread/fwrite.我写了

For my assignment, I'm required to use fread/fwrite. I wrote

#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文件

clients.txt file


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.

I can do this with fscanf (which Ive commented out), but I'm required to use fread/fwrite.

  1. 为什么从Jone的帐户开始会有大量数字?
  2. 为什么之后会有垃圾?feof不应该停止吗?
  3. 使用此方法是否有任何弊端?还是fscanf方法?

如何解决这些问题?提前非常感谢

How can I fix these? Many thanks in advance

推荐答案

正如评论所说, fread 无需任何解释即可读取文件中的字节.文件 clients.txt 包含50个字符,第一行16个字符,第二行14个字符,第三行18个字符,以及两个换行符.(您的clients.txt在第三行之后不包含换行符,您将很快看到.)换行符是UNIX或Mac OS X计算机上的一个单字节 \ n ,但是(可能)Windows计算机上为两个字节 \ r \ n -因此为50或51个字符.这是十六进制的ASCII字节序列:

As the comments say, fread reads the bytes in your file without any interpretation. The file clients.txt consists of 50 characters, 16 in the first line plus 14 in the second plus 18 in the third line, plus two newline characters. (Your clients.txt does not contain a newline after the third line, as you will soon see.) The newline character is a single byte \n on UNIX or Mac OS X machines, but (probably) two bytes \r\n on Windows machines - hence either 50 or 51 characters. Here is the sequence of ASCII bytes in hexadecimal:

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 调用在第一次通过后就跳出了循环,一次吞噬了整个文件.

Your fread statement copies these bytes without any interpretation directly into your rec1 data structure. That structure begins with int account;, which says to interpret the first four bytes as an int. As one of the comments noted, you are running your program on a little-endian machine (most likely an Intel machine), so the least significant byte is the first and the most significant byte is the fourth. Thus, your fread said to interpret the sequence of four ASCII characters "100 " as the four byte integer 0x20303031, which equals, in decimal, 540028977. The next member of your struct is char name[100];, which means that the next 100 bytes of data in rec1 will be the name. But the fread was told to read sizeof(rec1)=112 bytes (4 byte account, 100 byte name, 8 byte balance). Since your file is only 50 (or 52) characters, fread will have only been able to fill in that many bytes of rec1. The return value of fread, had you not discarded it, would have told you that the read stopped short of the number of bytes you requested. Since you hit EOF, the feof call breaks out of the loop after that first pass, having consumed the entire file in one gulp.

所有输出均由对 fprintf 的第一个也是唯一的调用产生.数字540028977和以下空格是由%d" rec1.account 参数产生的.下一位只是部分确定的,您很幸运:%s" 说明符和相应的 rec1.name 参数将以ASCII格式打印下一个字符,直到<找到code> \ 0 个字节.因此,输出将以文件的剩余 50-4 (或 52-4 )个字符(包括两个换行符)开始,并且可能永远持续下去,因为文件(或任何文本文件)中没有 \ 0 字节,这意味着在打印文件的最后一个字符后,您看到的是自动变量中发生的任何垃圾 rec1 在您的程序启动时.(这种无意的输出类似于OpenSSL中著名的令人讨厌的bug.)您很幸运,垃圾仅在再加上几十个字符后就包含了一个 \ 0 字节.请注意, printf 无法知道 rec1.name 被声明为仅100个字节的数组-它仅获得指向 name开头的指针-您有责任确保 rec1.name 包含终止的 \ 0 字节,而您从来没有那样做.

All of your output was produced by the first and only call to fprintf. The number 540028977 and the following space were produced by the "%d " and the rec1.account argument. The next bit is only partly determinate, and you got lucky: The "%s" specifier and the corresponding rec1.name argument will print the next characters as ASCII until a \0 byte is found. Thus, the output will begin with the 50-4 (or 52-4) remaining characters of your file -- including the two newlines -- and potentially continue forever, because there are no \0 bytes in your file (or in any text file), which means that after printing the last character of your file, what you are seeing is whatever garbage happened to be in the automatic variable rec1 when your program started. (That kind of unintentional output is similar to the famous heartbleed bug in OpenSSL.) You were lucky the garbage included a \0 byte after only a few dozen more characters. Note that printf has no way to know that rec1.name was declared to be only a 100 byte array -- it only got the pointer to the beginning of name -- it was your responsibility to guarantee that rec1.name contained a terminating \0 byte, and you never did that.

我们可以讲更多一点.数字 -9.2559631349317831e61 (在%f" 格式下很难看)是 rec1.balance 的值.IEEE 754机器(例如您的Intel和所有现代计算机)上的 double 值的8个字节以十六进制 0xcccccccccccccccc 表示.特殊的符号出现在 rec1.name 对应的%s" 输出中,而只有100-46 = 54个字符剩下的100个,因此您的%s" 输出已经超出了 rec1.name 的末尾,并且在 rec1.balance 中包含了讨价还价,我们了解到您的终端程序将非ASCII字符 0xcc 解释为.有很多方法可以解释大于127(0x7f)的字节.例如,在latin-1中,它应该是& Igrave; .图形字符是古老的MS-DOS字符集Windows代码页437中0xcc(204)字节的表示.您不仅在Intel机器上运行,而且还是Windows机器(当然,最有可能的起点).

We can tell a little bit more. The number -9.2559631349317831e61 (which is pretty ugly in "%f" format) is the value of rec1.balance. The 8 bytes for that double value on an IEEE 754 machine (like your Intel and all modern computers) are in hex 0xcccccccccccccccc. Sixty four of the peculiar symbol appear in the "%s" output corresponding to rec1.name, while only 100-46 = 54 characters remain of the 100, so your "%s" output has run off the end of rec1.name, and includes rec1.balance into the bargain, and we learn that your terminal program interpreted the non-ASCII character 0xcc as . There are many ways to interpret bytes bigger than 127 (0x7f); in latin-1 it would have been &Igrave; for example. The graphical character is the representation of the 0xcc (204) byte in the ancient MS-DOS character set, Windows code page 437. Not only are you running on an Intel machine, it is a Windows machine (of course the mostly likely possibility to begin with).

这回答了您的前两个问题.我不确定我是否理解您的第三个问题.我希望这种缺点"是显而易见的.

That answers your first two questions. I'm not sure I understand your third question. The "drawbacks" I hope are obvious.

关于如何修复它,没有合理简单的方法使用 fread 读取和解释文本文件.为此,您需要复制 libc fscanf 函数中的许多代码.唯一明智的方法是首先使用 fwrite 创建一个二进制文件.那么 fread 将自然地将其读回.因此,必须有两个程序-一个程序编写一个二进制 clients.bin 文件,另一个程序将其读回.当然,这并不能解决第一个程序的数据首先应来自何处的问题.它可能来自使用 fscanf 读取 clients.txt .或者可以将其包含在 fwrite 程序的源代码中,例如通过初始化 struct rec 的数组,如下所示:

As for how to fix it, there is no reasonably simple way to read and interpret a text file using fread. To do so, you would need to duplicate much of the code in the libc fscanf function. The only sensible way is to first use fwrite to create a binary file; then fread will work naturally to read it back. So there have to be two programs -- one to write a binary clients.bin file, and a second to read it back. Of course, that does not solve the problem of where the data for that first program should come from in the first place. It could come from reading clients.txt using fscanf. Or it could be included in the source code of the fwrite program, for example by initializing an array of struct rec like this:

struct rec recs[] = {{100, "Jones", 564.90},
                     {200, "Rita", 54.23},
                     {300, "Richard", -45.00}};

或者它可能来自读取MySQL数据库,或者...一个不太可能起源的地方是一个二进制文件(很容易),它可以用 fread 读取.

Or it could come from reading a MySQL database, or... The one place it is unlikely to originate is in a binary file (easily) readable with fread.

这篇关于fread()c中的结构的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆