fread和fgets的规格有什么区别?

3

当从文件中读取内容时,fread和fgets有什么区别?我使用相同的fwrite语句,但是当我使用fgets读取.txt文件时,它按预期工作,但是当我使用fread()时,它不起作用。

我已经从fgets/fputs切换到fread/fwrite来读取和写入文件。我使用fopen(rb/wb)以二进制形式而非标准字符形式读取。我了解fread将获取/0空字节,而不仅仅是单行。

  //while (fgets(buff,1023,fpinput) != NULL) //read in from file
  while (fread(buff, 1, 1023, fpinput) != 0) // read from file

我希望能够从文件中读取到缓冲区,将缓冲区放入共享内存中,然后让另一个进程从共享内存中读取并写入新文件。

当我使用fgets()函数处理.txt文件时,它能按预期工作。但是当使用fread()函数时,它会将长度为300个字符左右的一行加上一个新行符添加到缓冲区中。我无法理解其中的原因。


4
fgets 遇到换行符时停止读取。fread 不会检查这些换行符。 - ikegami
2
fgets 会添加一个 NUL 字符,而 fread 则不会。 - ikegami
当buf不是以NUL结尾的字符串时,你是否将其视为NUL结尾的字符串处理了? - ikegami
真的吗?fread读取指定数量的(原始二进制)字节,而fgets读取文本行直到行末字符。并追加null。您可以在文本文件上使用fread,但通常不建议在二进制文件上使用fgets。不要跳过您的C语言课程 ;) - ddbug
我正在尝试将所有内容转换为只使用fread()/fwrite()。但是当我使用fgets时,结果如下:https://prnt.sc/neq45x 但是当我使用fread()时,结果如下:https://prnt.sc/neq4bz - Remilia Scarlet
当你使用 fread 时,必须记住并使用读取的单位数,而不仅仅是检查是否为 0。但是,除非您只想将文本文件未经处理地复制到另一个文件中,否则 fread 对于文本文件并不是很有用。 - Weather Vane
1个回答

2

fgets在遇到换行符时会停止读取。而fread则不会。因此,fgets通常只对文本文件有用,而fread可用于文本和二进制文件。

根据C11标准:

7.21.7.2 fgets函数

fgets函数从指向流的指针stream所指向的流中最多读取n个字符减1个字符,并将其存储到指向数组s的指针中。在新行字符(保留)或文件结束后,不会再读取任何其他字符。一个空字符会被写入到数组中最后一个读取的字符之后。

7.21.8.1 fread函数

fread函数从流指针stream指向的流中读取大小为size的元素,最多读取nmemb个元素,并将结果存储在由ptr指向的数组中。对于每个对象,fgetc函数会调用size次,并将以读取顺序存储的结果存储在无符号字符数组中,该数组恰好覆盖该对象。如果定义了流的文件位置指示器,则该指示器将根据成功读取的字符数进行提升。如果发生错误,则流的文件位置指示器的结果值是不确定的。如果只读取了部分元素,则其值是不确定的。
这段代码片段可能会让您更清楚地理解它。它只是将文件按块复制。
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char ** argv)
{
    if(argc != 3) {
        printf("Usage: ./a.out src dst\n");
        printf("Copies file src to dst\n");
        exit(EXIT_SUCCESS);
    }

    const size_t chunk_size = 1024;

    FILE *in, *out;
    if(! (in = fopen(argv[1], "rb"))) exit(EXIT_FAILURE);
    if(! (out = fopen(argv[2], "wb"))) exit(EXIT_FAILURE);

    char * buffer;
    if(! (buffer = malloc(chunk_size))) exit(EXIT_FAILURE);

    size_t bytes_read;

    do {
        // fread returns the number of successfully read elements
        bytes_read = fread(buffer, 1, chunk_size, in);

        /* Insert any modifications you may */
        /* want to do here                  */

        // write bytes_read bytes from buffer to output file
        if(fwrite(buffer, 1, bytes_read, out) != bytes_read) exit(EXIT_FAILURE);
   // When we read less than chunk_size we are either done or an error has 
   // occured. This error is not handled in this program. 
    } while(bytes_read == chunk_size); 


    free(buffer);
    fclose(out);
    fclose(in);
}

您在下面的评论中提到想要将其用于字节交换。那么,您可以使用以下代码片段。只需将其插入上面的代码中指定的位置即可。
for(int i=0; i < bytes_read - bytes_read%2; i+=2) {
    char tmp = buffer[i];
    buffer[i] = buffer[i+1];
    buffer[i+1] = tmp;
}

在处理字节而不是字符时,我是否必须使用size_t buff [1024]类型?我的最终目标是对字节进行简单的交换,其中abcdef变为-> badcfe。然后我希望再次运行程序以将其交换回来。它应该能够处理.txt和.png文件。这就是为什么我从更容易的.txt开始解决问题,然后再解决.png的原因。我想,由于字符在内存中只占用一个字节,所以它应该可以工作?@Broman - Remilia Scarlet
@RemiliaScarlet,我不确定你在这里的意思。缓冲区的类型是char*。如果你的目标是二进制文件,你应该从二进制文件开始。过程基本相同,但工具非常不同,如果它适用于二进制文件,它也将适用于文本文件。毕竟,文本文件是二进制文件的子集。 - klutt
啊,我的错误。是的,我的问题在于我不熟悉使用二进制而非字符串所需的新工具。我会继续尝试解决它。 - Remilia Scarlet
@RemiliaScarlet 可以随意使用上面的代码。看起来你可以直接使用它,只需在我上面指示的位置插入你的修改代码即可。 - klutt
我现在正在尝试它,这确实可以复制数据并将其写出,我刚刚尝试了一下png文件,它正确地完成了操作,txt文件也是如此。我会让它正常工作的!非常感谢你的帮助,我已经折腾了大约10个小时哈哈。 - Remilia Scarlet
@RemiliaScarlet 当你知道如何做时,这很容易。;) 我创建了一个字节交换片段并将其添加到答案中,所以你也有了它。 - klutt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接