【问题标题】:Cannot Copy a Text File through fwrite and fread in C [closed]无法通过 C 中的 fwrite 和 fread 复制文本文件 [关闭]
【发布时间】:2017-11-28 04:22:17
【问题描述】:

代码如下:

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

int main() {
    FILE *f, *fp;
    char buff[512];
    int buff_size;
    int bytes;

    fp = fopen("File.txt", "rb");

    if (fp == NULL) {
        printf("Cannot Open Source File!\n");
        exit(1);
    }

    f = fopen("append.txt", "ab+");

    if (f == NULL) {
        printf("Cannot Open Target File!\n");
        fclose(fp);
        exit(1);
    }

    buff_size = sizeof(buff);

    while (bytes = fread(&buff, buff_size, 1, fp) > 0) {
        if (bytes > 0)
            fwrite(&buff, buff_size, 1, f);
        else
            break;

        printf("Appending...\n\n");
    }

    rewind(f);

    while (bytes = fread(&buff, buff_size, 1, f) > 0)
        if (bytes > 0)
            printf("%s", buff);


    fclose(fp);
    fclose(f);
}

所以,它碰巧没有输出任何东西,当我检查文件“append.txt”时,它也不包含任何东西。 请注意,源文件“File.txt”不为空。 谁能告诉我它有什么问题?

编辑:

我通过将buff_size 替换为strlen(buff) 解决了这个问题,如下所示: bytes = fread(&amp;buff, strlen(buff), 1, f) &gt; 0fwrite() 和第二个 fread() 相同。

有人能解释一下为什么会这样吗?

【问题讨论】:

  • 你的编译器是否给你任何关于将&amp;buff 传递给fread()fwrite() 的警告?
  • @FelixPalmen 不,它没有
  • while( bytes = fread(&amp;buff, buff_size, 1, fp) &gt; 0 ) 行没有按照您的意愿进行操作 - 您缺少 while((bytes = fread(&amp;buff, buff_size, 1, fp)) &gt; 0) 中的括号。您还需要反转 count 和 size 参数 - 您会在文件末尾得到一个简短的读取,该文件的长度不是 512 字节的精确倍数,但您不会知道这些字节。
  • 你的“修复”是可怕错误的。
  • "有人能解释一下为什么会这样吗?" -- 因为 未定义的行为 是未定义的。 C 不是一种通过反复试验进行编码的语言。

标签: c file file-io fwrite fread


【解决方案1】:
char buff[512];
int buff_size;

// [...]

bytes = fread(&buff, buff_size, 1, fp)

这将尝试读取一个 512 字节的块。返回值是读取的块数,因此不会是字节。但撇开这一点不谈,如果您的文件短于 512 字节,这将不会读取 任何内容

你想要的是读取 512 次 1 个字节,然后你会得到字节数,所以交换 buff_size1 的位置。


旁注:

  • 如果您正确地检查循环条件,例如:

    while ((bytes = fread(buff, 1, buff_size, fp)) > 0 )
    

    if (bytes &gt; 0) 的额外检查是多余的。

  • 写入时,你只想写入你实际读取的字节数:

    fwrite(buff, 1, bytes, f);
    
  • 对于尺寸,请始终使用size_t -- int 很可能是错误的:

    size_t buff_size;
    size_t bytes;
    
  • printf("%s", ) 打印buff未定义的行为,因为您没有在fread() 读取的数据之后添加'\0' 字节。 C 字符串必须以'\0' 结尾。当fread() 读取的数据意外不包含'\0' 时,printf() 将读取和使用未初始化的数据,甚至可能读取超出buff 的范围。

【讨论】:

  • 还提到验证阶段有未定义的行为,因为buff 不一定是正确的以空字符结尾的字符串。
  • @chqrlie 添加,谢谢。
  • 从技术上讲,int 在这里是可以的,因为fread 的返回值在0sizeof buff 的范围内,它小到可以放入int,但它使用正确的类型确实更好。
  • @chqrlie 是的,我写 could 是有原因的。只是指出它是因为这是一个坏习惯,我怀疑 OP 在他不知道可能范围的情况下会使用正确的类型:)
【解决方案2】:

Felix Palmen 在您的代码中列出了一些问题,您的修复完全错误,因为 buff 甚至没有空终止符。

这是一个更好的版本:

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

int main(void) {
    FILE *f, *fp;
    char buff[512];
    size_t bytes;

    fp = fopen("File.txt", "rb");
    if (fp == NULL) {
        fprintf(stderr, "Cannot open source file!\n");
        exit(1);
    }

    f = fopen("append.txt", "ab+");
    if (f == NULL) {
        fprintf(stderr, "Cannot open target file!\n");
        fclose(fp);
        exit(1);
    }

    while ((bytes = fread(buff, 1, sizeof buff, fp)) != 0) {
        if (fwrite(buff, 1, bytes, 1, f) != bytes) {
            fprintf(stderr, "Error writing to the target file\n");
            break;
        }
        printf("Appending...\n\n");
    }

    rewind(f);

    while ((bytes = fread(buff, 1, sizeof buff, f)) != 0) {
        printf("%.*s", (int)bytes, buff);
    }
    fclose(fp);
    fclose(f);
    return 0;
}

【讨论】:

    【解决方案3】:
    bytes = fread(&buff, buff_size, 1, fp) > 0
    

    看看C Precedence Chart。运算符&gt; 高于=,因此fread 的返回值将与零进行比较,然后将比较结果存储在bytes 中。你打算写这个

    (bytes = fread(&buff, buff_size, 1, fp)) > 0
    

    【讨论】:

    • 实际上您的修复只是部分的:对于bytes 接收读取的字节数,参数必须以相反的顺序传递:(bytes = fread(buff, , buff_size, fp)) > 0`。
    【解决方案4】:

    在给定固定文件名的情况下,这是我对您的问题的解决方案。如果是我自己的代码,它会将文件名作为参数。

    #include <stdio.h>
    
    int main(void)
    {
        const char src_file[] = "File.txt";
        FILE *fp = fopen(src_file, "rb");
    
        if (fp == NULL)
        {
            fprintf(stderr, "Cannot Open Source File '%s'!\n", src_file);
            return(1);
        }
    
        const char tgt_file[] = "append.txt";
        FILE *f = fopen(tgt_file, "ab+");
    
        if (f == NULL)
        {
            fprintf(stderr, "Cannot Open Target File '%s'!\n", tgt_file);
            fclose(fp);
            return(1);
        }
    
        char buff[512];
        int bytes;
        while ((bytes = fread(buff, sizeof(char), sizeof(buff), fp)) > 0)
        {
            fwrite(buff, sizeof(char), bytes, f);
            printf("Appending...\n\n");
        }
    
        rewind(f);
    
        while ((bytes = fread(buff, sizeof(char), sizeof(buff), f)) > 0)
            printf("%.*s", bytes, buff);
    
        fclose(fp);
        fclose(f);
        return 0;
    }
    

    有各种各样的修复,其中大部分都在沿线某处的 cmets 中阐明。

    • 我将文件名放入数组中,以便在fopen() 和错误消息中都可以使用该名称,这些消息将打印到stderr,而不是stdout。这对通用代码中的其他用户很有帮助。如果文件名来自命令行参数,这很简单。

    • fread() 的调用已修复,以便报告字节数,而不是 512 字节块的数量(将是 0 或 1)。这涉及将大小/计数参数的顺序反转为fread()。传递的是缓冲区而不是缓冲区的地址。

    • 正确捕获读取的字节数。

    • 读取的字节数用于控制fwrite()的大小。

    • 读取的字节数用于控制printf()打印的字节数。

    • 我不喜欢 C99 及更高版本中允许 main()(但只允许 main())默认返回 0 的特殊规则。 AFAIAC,它是一个定义为返回int 的函数;它应该返回一个int。但是,也有人不同意。

    【讨论】:

    • 虽然技术上是正确的,但fread(buff, sizeof(char), sizeof(buff), fp) 看起来是假的:它应该读作fread(buff, 1, sizeof(buff), fp)fread(buff, sizeof(*buff), countof(buff), fp) 或者可能是fread(buff, sizeof(char), sizeof(buff) / sizeof(char), fp)。对于任何更大的类型,您的表单都会失败。
    • @chqrlie:我不同意,但如果我使用不同的类型,结果变量将不是bytes,而是items 或其他一些聚合名称。如果我将struct SomeStruct 的单元读入数组struct SomeStruct array[ARRAY_SIZE],我会使用int num_read = fread(array, sizeof(array[0]), ARRAY_SIZE, fp); 或类似的。我对读取的字节数不感兴趣;我会对阅读的完整结构的数量感兴趣。在这个例子中,我对char 感兴趣,所以我使用sizeof(char) 作为大小;数组中可用的char 的数量恰好是sizeof(buffer)
    • 你不能死记硬背地用 C 语言编写代码。你总是需要思考。
    • 我不同意:不管目标变量的名称如何,使用sizeof 的2 个实例作为fread 的参数只有在其中一个或另一个评估为1 时才会发生。这是一个引人注目的吸引眼球的方法,并为那些不会立即掌握 sizeof(char) == 1 并且可能对其他类型使用相同语法的普通读者树立了一个坏榜样。
    猜你喜欢
    • 2013-02-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-29
    • 2018-04-22
    • 2015-08-12
    • 1970-01-01
    相关资源
    最近更新 更多