繁体   English   中英

在 fwrite() 上循环 fread() 与单独使用它们有什么区别?

[英]What is the difference between looping fread() over fwrite() vs using them separately?

(注:向半小时前阅读我上一篇文章的任何人道歉——我意识到我发布了错误的一半代码,实际上是后半部分给我带来了问题。所以,如果这篇文章看起来可能是熟悉的!)

我正在尝试制作一个复制.wav文件的基本程序,这是我的尝试:

#include <stdio.h>
#include <stdint.h>

int main (void)
{
    FILE* input = fopen("input.wav", "r");
    FILE* output = fopen("output.wav", "w");
    
    uint8_t header [44];
    
    fread(header, sizeof(uint8_t), 44, input);
    fwrite(header, sizeof(uint8_t), 44, output);
    
    int16_t body;
    
    fread(&body, sizeof(int16_t), 1, input);
    fwrite(&body, sizeof(int16_t), 1, output);
    
    fclose(input);
    fclose(output);
}

但是,在未能使其工作之后,我查找了如何做到这一点,显然

fread(&body, sizeof(int16_t), 1, input);
fwrite(&body, sizeof(int16_t), 1, output);

应该

while (fread(&body, sizeof(int16_t), 1, input))
{
    fwrite(&body, sizeof(int16_t), 1, output);
}

为什么第一部分可以毫无问题地复制header,但第二部分必须使用循环才能工作? 在我看来,他们似乎在做同样的事情。

我认为这里的根本问题是,您用于读取和写入 header 的代码一次读取 44 字节 header,而用于读取和写入正文的代码仅读取和写入两个字节。 但是 wav 文件的主体可能比两个字节大得多!

具体来说,这段代码:

fread(header, sizeof(uint8_t), 44, input);
fwrite(header, sizeof(uint8_t), 44, output);

一次读取和写入一个 44 字节的 header。 但是这段代码:

fread(&body, sizeof(int16_t), 1, input);
fwrite(&body, sizeof(int16_t), 1, output);

读取和写入两个字节。 如果正文只有两个字节长,那很好。 但如果不是,它就不会复制整个身体; 它只是复制前两个字节。

另一方面,这段代码读取和写入多个两字节对象,其中有多少:

while (fread(&body, sizeof(int16_t), 1, input) == 1)
    fwrite(&body, sizeof(int16_t), 1, output);

如果您知道正文有多大,您可以一次读取和写入所有内容(也就是说,就像您对标题所做的那样):

uint8_t body[body_size];
fread(body, 1, body_size, input);
fwrite(body, 1, body_size, output);

或者,如果您知道正文大小为 16 位字,并且您想这样考虑,您可以这样做

uint16_t body [body_size_in_words];
fread(body, 2, body_size_in_words, input);
fwrite(body, 2, body_size_in_words, output);

freadfwrite可能会令人困惑,因为它们旨在让您假装您正在读取和写入除单个字节之外的其他内容。 有三种不同的方式来使用它们:

  • fread(buf, 1, n, fp)读取n个字节
  • fread(buf, size, 1, fp)读取一条size字节的记录
  • fread(buf, size, n, fp)读取nsize字节的记录

在内心深处, fread基本上只是乘以n * size ,并尝试读取那么多字节。 然后,无论它读取多少字节,它都会在返回给您之前将该数字除以size - 也就是说,它返回读取的记录数,而不一定是读取的字节数。 当然,同样的例子、解释和 arguments 也适用于fwrite

看看读和写函数。 如您所见,这两个函数的签名非常相似,并且使用相同的 arguments。 两者都用于对数据进行操作。

 size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
 size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
争论 描述
*指针 指向存储数据的缓冲区
尺寸 每个块的大小(以字节为单位)
纳米粒 要读/写的块数
*溪流 指向描述传出 stream 的结构

因此,您的代码的区别是:

// will read ONE block of data with the size of int16_t
fread(&body, sizeof(int16_t), 1, input);
// will read 44 blocks of data with the size of uint8_t
fread(header, sizeof(uint8_t), 44, input);

// will write ONE block of data with the size of int16_t
fwrite(&body, sizeof(int16_t), 1, output);
// will write 44 blocks of data with the size of int16_t
fwrite(header, sizeof(uint8_t), 44, output);

这两种方法都返回一个size_t类型的值,它反映了读取或写入的字节数。 如果他们返回 0,则什么都没有发生。 如您所知,在 C 中,不等于 0 的所有内容都被认为是正确的。 所以回答你的问题:

// reads ONE Block of size int16_t at a time and stores it in body
// => will return 0 and stop the loop if nothing was read anymore
while (fread(&body, sizeof(int16_t), 1, input))
{
    // writes ONE block of size int16_t from body to output
    fwrite(&body, sizeof(int16_t), 1, output);
}

只要还有数据要读取,循环就会继续,然后停止。 文件的 header 具有固定大小,因为它是标准化的。 文件的 rest 具有可变长度,因此是循环。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM