正确使用fread

时间:2011-02-16 12:26:02

标签: c wav fread

以下功能无效,即退出:

fread(buf, 1, 4, stdin);
buf[4] = '\0';

if (strcmp((char*)buf, "data")) exit(EXIT_FAILURE);

我认为如果我可以手动将fread推到更远的流中,它最终会点击“数据”。

换句话说,如何增加fread以便跳过字节。

代码示例总是受到赞赏。

谢谢!


编辑1

基本上我正在解析iPhone上wav文件的标题。它给了我一些麻烦,我相信这与苹果格式化音频文件的方式有关。有人建议我在流中直到获得“数据”,然后从那里继续前进。

我希望这能澄清事情。


编辑2

Here is documentation as to how the wav file header should look like,但我想知道苹果格式化他们的方式是否会导致这种情况不准确。

您会注意到'数据'偏移36,这是4的倍数。

3 个答案:

答案 0 :(得分:2)

这可以像时尚一样流动,并按照我的想法做到:

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

int main(int argc, char** argv)
{
    long pos = 0;
    char buf;
    char str[5] = {'\0','\0','\0','\0','\0'};

    while ( fread(&buf, sizeof(char), 1, stdin) > 0 )
    {
        pos++;
        str[0] = str[1]; 
        str[1] = str[2];
        str[2] = str[3];

        str[3] = buf;
        str[4] = '\0';

        /* uncomment to see what got read ** printf("Read %s\n", str); */

        if ( strcmp(str, "data") == 0 )
        {
            break;
        }
    }


    printf("\"data\" occured after %ld bytes\n", pos);
    return 0;
}

这可以通过使用我称为str的缓冲区并旋转其中的位置来实现。它会一直有效,直到data出现。

请注意,它会读取二进制数据,而不是文本。所以stdin上的任何内容都会被读取,包括换行符。但是,如果您将其调整为不应该成为问题的文件句柄。

你可以包括这个。使用fread的问题在于设计:

  

流的文件位置指示符(如果已定义)应按成功读取的字节数提前。

因此,如果您一次前进4个字节,除非您的数据恰好是数据开头的4的倍数,否则您将错过它。例如:

123DATA

如果一次读取4个字节,则会失败。

现在,鉴于这是一个文档化的文件格式,是否有某些标题规范可以告诉您标题字段的确切宽度?或者至少在它们变化的地方,你可以适当地阅读它们吗?阅读直到data有效,但实际上并不优雅。

或者,更好的是,我确信必须有一个库可以在某处执行此操作。


编辑响应波形文件的标题,由于它是固定的而不是那么大,所以将整个内容读入缓冲区。

uint8_t* hdr = malloc(36*sizeof(uint8_t));
fread(hdr, sizeof(uint8_t), 36);

别忘了自由。此时,您已提取整个标头。我使用uint8_t肯定是8位。在这个阶段,你可以提取一些有趣的技巧,比如将数据转换为结构。请注意字段的字节顺序。

从那时起,我相信这个流可以以大块的形式提供给你。你需要做的第一件事是:

uint8_t chkid;
uint8_t chksz;
fread(&chkid, sizeof(uint8_t), 4, stream);
fread(&chksz, sizeof(uint8_t), 4, stream);

那将抓住你那个特定块的数据。假设您正在使用一个小端系统,此时您应该能够直接使用chksz作为整数,所以现在可以这样做:

uint8_t dataframe = malloc(chksz * sizeof(uint8_t));

您可以在其中阅读数据:

fread(&dataframe, sizeof(uint8_t), chksz, stream);

这当然是假设Apple波形是描述的那种。现在,从该页面开始:

  

WAVE文件格式是Microsoft的RIFF规范的一个子集,用于存储多媒体文件。 RIFF文件以文件头开头,后跟一系列数据块。 WAVE文件通常只是一个RIFF文件,其中包含一个“WAVE”块,它由两个子块组成 - 一个指定数据格式的“fmt”块和一个包含实际样本数据的“数据”块。将此表格称为“规范形式”。谁知道它真的如何运作。

我已经给你指示如果在连续循环中使用,直到流上没有其他内容,将允许你读取任意数量的数据块a-la RIFF。然后,您需要处理适当的数据以将其分解;即拆分您正确阅读的数据块。如果这是您希望阅读的唯一格式,则可以忽略其他块。

现在,问题仍然存在,什么是苹果格式,说实话我不知道!

答案 1 :(得分:1)

我认为不能保证ASCII中第一次出现data是数据头的开头,因为这四个字节也可能作为WAV format格式块的一部分出现。解析WAV的更好方法是(未经测试)

/* Returns the size of the data payload */
off_t skip_to_data_payload(FILE *fp)
{
    unsigned char buf[4];
    int i;
    off_t size;

    // the "data" magic should start at byte 36
    for (i=0; i<9; i++)
        fread(buf, 1, 4, fp);
    fread(buf, 1, 4, fp);
    if (memcmp(buf, "data", 4) != 0)
        return (off_t)(-1);

    // read size, assume little-endian
    fread(buf, 1, 4, fp);
    off_t size = buf[0] + (buf[1] << 8) + (buf[2] << 16) + (buf[4] << 24);

    return size;
}

注意:我假设PCM编码和小端文件。由于Apple过去使用过大端处理器,所以你真的应该检查一下。 (或使用库。)

答案 2 :(得分:0)

strcmp将永远不会工作,因为你需要一个额外的字符串终止(\ 0),你已经用完了你的字符串的所有4个字符。请尝试使用strncmp。如果在使用stdin时添加更多字符,请不要忘记换行符。