首页 > 解决方案 > 我的代码中的 fread() 出了什么问题?

问题描述

这是我现在正在尝试的与我之前在 cs50 'Recover' 中的问题相关的代码的摘录。

我已经尝试了一切fread,但由于某种原因,它根本没有按照我想要的方式工作。

在这个循环中,我试图弄清楚fread实际是如何工作的。

我的问题是 -每次调用时它是否fread从指向 ( ) 的文件中一次读取 512 个字节的数据 1 ?rawdata在这种情况下,我的代码应该可以工作,因为循环无限期地运行,一遍又一遍地调用函数,因此一次移动流位置/文件光标(我不知道它叫什么)512 个字节。我从循环中休息了feof(rawdata)

我正在使用这个小程序来帮助我从 cs50 pset4 恢复。

    // In a loop, until the end of file has been reached,
    while (true) {
        // Zeroing counter
        jpg_counter = 0;
        // Reading 512 bytes into the array of bytes
        fread(bytes, 512, 1, rawdata);
        
        // Searching the 512 bytes for JPEG signatures - bytes[3] with bitwise AND
        if (bytes[0] == 0xff && bytes[1] == 0xd8 && bytes[2] == 0xff && (bytes[3] & 0xf0) == 0xe0) {
            jpg_counter++;
        }
        
        // If found JPG, add total and keep going till end of file
        if (jpg_counter != 0) {
            total++;
        }
 
        //feof returns a non zero value only at the end of a file
        if (feof(rawdata) != 0) {
            break;
        }
    }
    printf("%i\n", total);

标签: ccs50fread

解决方案


您的方法有两个主要问题:

  • 您没有在阅读循环中正确测试文件结尾。您应该使用的返回值fread来确定您是否从流中读取了至少一个字节,并且您应该以相反的顺序传递参数:您正在读取多达 512 个字节:

      size_t n;
      while ((n = fread(bytes, 1, 512, rawdata)) > 0) {
          // n bytes were read from the file.
    
  • 您只测试 JPEG 签名的前 4 个字节。除非已知该文件具有非常特定的结构,否则您可能希望在该块中搜索签名。

  • 必须注意处理重叠块的签名,即:从 512 字节块的末尾开始但在下一个块结束。

这是修改后的版本:

int count_jpeg(FILE *rawdata) {
    unsigned char bytes[3 + 512];
    int pos = 0, end, i;
    int total = 0;
    size_t nread;
    // In a loop, until the end of file has been reached,
    while ((nread = fread(bytes + pos, 1, 512, rawdata)) > 0) {
        end = pos + nread - 3;
        for (i = 0; i < end; i++) {
            // Searching the block for JPEG signatures - bytes[3] with bitwise AND
            if (bytes[i] == 0xff && bytes[i + 1] == 0xd8
            &&  bytes[i + 2] == 0xff && (bytes[i + 3] & 0xf0) == 0xe0) {
                total++;
                i += 3;
            }
        }
        // copy the last 3 bytes to the beginning of the block
        for (i = pos = 0; i < 3; i++)
            if (end + i >= 0) {
                bytes[pos++] = bytes[end + i];
        }
    }
    printf("%i\n", total);
    return total;
}

编辑:如果已知 JPEG 签名在 512 字节边界上对齐,则可以删除扫描循环,但仍然可以读取文件末尾的部分块:

int count_jpeg(FILE *rawdata) {
    unsigned char bytes[512];
    int total = 0;

    // In a loop, until the end of file has been reached,
    while (fread(bytes + pos, 1, 512, rawdata) > 4) {
        /* check for a signature at the beginning of the block */
        if (bytes[0] == 0xff && bytes[1] == 0xd8
        &&  bytes[2] == 0xff && (bytes[3] & 0xf0) == 0xe0) {
            total++;
        }
    }
    printf("%i\n", total);
    return total;
}

推荐阅读