【问题标题】:Issue with fread () and fwrite () functions in CC 中 fread () 和 fwrite () 函数的问题
【发布时间】:2020-12-14 09:47:43
【问题描述】:

我编写了一个基本代码,它以二进制模式(使用 fwrite())将字符串写入文件。我也可以从文件中读取相同的字符串(使用 fread())到缓冲区并打印它。它可以工作,但在我从文件中读取的部分,额外的垃圾也会被读入缓冲区。我的问题是如何正确知道要读取的字节的长度?

以下是代码--

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#define BUFSZ 81

char * get_string (char *, size_t);

int main (int argc, char * argv[])
{
    if (argc != 2)
    {
        fprintf (stderr, "Invalid Arguments!!\n");
        printf ("syntax: %s <filename>\n", argv[0]);
        exit (1);
    }

    FILE * fp;

    if ((fp = fopen(argv[1], "ab+")) == NULL)
    {
        fprintf (stderr, "Cannot openm file <%s>\n", argv[1]);
        perror ("");
        exit (2);
    }


    char string[BUFSZ];
    char readString[BUFSZ];
    size_t BYTES, BYTES_READ;

    puts ("Enter a string:  ");
    get_string (string, BUFSZ);

    // printf ("You have entered:  %s\n", string);

    BYTES = fwrite (string, sizeof (char), strlen (string), fp);

    printf ("\nYou have written %zu bytes to file <%s>.\n", BYTES, argv[1]);

    printf ("\nContents of the file <%s>:\n", argv[1]);

    rewind (fp);

    BYTES_READ = fread (readString, sizeof (char), BUFSZ, fp);
    printf ("%s\n", readString);

    printf ("\nYou have read %zu bytes from file <%s>.\n", BYTES_READ, argv[1]);    

    getchar ();
    fclose (fp);
    return 0;
}


char * get_string (char * str, size_t n)
{
    char * ret_val = fgets (str, n, stdin);
    char * find;

    if (ret_val)
    {
        find = strchr (str, '\n');
        if (find)
            * find = '\0';
        else
            while (getchar () != '\n')
                continue; 
    }

    return ret_val;
}

【问题讨论】:

  • 您能帮我们提供似乎导致此问题的输入字符串吗?您的程序的相应输出也会有所帮助。
  • 您不能调用fprintf 后跟perror 并期望得到合理的结果。 fprintf 可能会更改errno,因此perror 打印的消息不再相关。只需:if( ((fp = fopen(argv[1]... ==NULL) { perror(argv[1]); ...}

标签: c file-io


【解决方案1】:

在我从文件中读取的部分,额外的垃圾也被读入缓冲区。

不,不是。由于您以附加模式打开文件,因此您可能正在读取额外数据之前您已编写的字符串,但您没有读取任何超出您所写内容的内容,因为那里没有什么可以读的。当文件最初为空或不存在时,您可以通过比较BYTES 的值与BYTES_READ 的值来验证。

您实际看到的是回读数据未以空值终止的效果。您没有将终止符写入文件,因此您无法将其读回。避免写入终止符可能是合理的,但在这种情况下,您必须在读回数据时提供一个新的终止符。例如,

readString[BYTES_READ] = '\0';

我的问题是如何正确知道要读取的字节的长度?

有多种可能性。其中比较突出的是

  • 使用定长数据
  • 将字符串长度写入文件,在字符串数据之前。

或者,在您的特定情况下,当文件开始为空并且您只在其中写入一个字符串时,还有可能捕获和处理读取了多少字节 而不是知道提前读多少。

【讨论】:

  • 因此,在将字节读入缓冲区后,我以 null 结束字符串以创建字符串的明确结束点。谢谢你提醒。忘记那部分了。
【解决方案2】:

首先,您会从用户那里获得string,其中最多包含BUFSZ-1 个字符(get_string() 函数将删除尾随换行符或跳过任何超过 BUFSZ 限制的字符。

例如,用户可能已经插入了单词Hello\n,所以在get_string()之后调用string数组包含

-------------------
|H|e|l|l|o|'\0'|...
-------------------

然后你将fwritestring缓冲区写入输出文件,写入strlen (string)字节。 这不包括字符串终止符'\0'

在我们的示例中,输出文件的内容是

--------------
|H|e|l|l|o|...
--------------

最后你从文件中读回。但由于readString 数组未初始化,文件内容后面将跟随未初始化数组中可能存在的每个垃圾字符。

例如,readString 可能具有以下初始内容:

---------------------------------------------
|a|a|a|a|a|T|h|i|s| |i|s| |j|u|n|k|!|'\0'|...
---------------------------------------------

从文件中读取后

---------------------------------------------
|H|e|l|l|o|T|h|i|s| |i|s| |j|u|n|k|!|'\0'|...
---------------------------------------------

这样下面的字符串就会被打印出来

HelloThis is junk!

为了避免这些问题,您必须确保目标缓冲区中存在尾随终止符。所以,只需以这种方式初始化数组:

char readString[BUFSZ] = { 0 };

这样,目标数组中至少会出现一个字符串终止符。

或者,在每次读取之前将其 memset 为 0:

memset (readString, 0, BUFSZ);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-29
    • 1970-01-01
    • 2019-05-30
    • 2016-03-12
    • 2012-01-22
    • 2012-03-06
    相关资源
    最近更新 更多