You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

CS50x Pset4 WAV文件反转代码报错:打开音频文件时出错

问题:WAV文件反转代码报错及文件损坏问题

运行用于反转WAV文件的C代码时,出现提示An error occurred while opening the audio file,怀疑问题出在TODO 8的音频反转逻辑。当前代码通过fseek定位到输出文件头部后44字节处写入音频块,但运行后不仅报错,还导致所有文件无法读取,提示This editor could not be opened due to an unexpected error: unable to read file,仅能通过重新加载页面恢复。

相关代码

主程序代码

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

#include "wav.h"

bool check_format(WAVHEADER header);
int get_block_size(WAVHEADER header);

int main(int argc, char *argv[])
{
    // Ensure proper usage
    // TODO #1
    if (argc != 3)
    {
        printf("Only put in the name of the original file, and the name of a new reversed file.\n");
        return 1;
    }
    // Open input file for reading
    // TODO #2
    FILE *raw_file = fopen(argv[1], "r");
    if (raw_file == NULL)
    {
        printf("Could not open original file.\n");
        return 1;
    }

    // Read header
    // TODO #3

    WAVHEADER header;
    //4 bytes
    fread(header.chunkID, 1, 4, raw_file);
    //1 dword
    fread(&header.chunkSize, 4, 1, raw_file);
    //4 bytes
    fread(header.format, 1, 4, raw_file);
    //4 bytes
    fread(header.subchunk1ID, 1, 4, raw_file);
    //1 dword
    fread(&header.subchunk1Size, 4, 1, raw_file);
    //1 word
    fread(&header.audioFormat, 2, 1, raw_file);
    //1 word
    fread(&header.numChannels, 2, 1, raw_file);
    //1 dword
    fread(&header.sampleRate, 4, 1, raw_file);
    //1 dword
    fread(&header.byteRate, 4, 1, raw_file);
    //1 word
    fread(&header.blockAlign, 2, 1, raw_file);
    //1 word
    fread(&header.bitsPerSample, 2, 1, raw_file);
    //4 bytes
    fread(header.subchunk2ID, 1, 4, raw_file);
    //1 dword
    fread(&header.subchunk2Size, 4, 1, raw_file);

    // Use check_format to ensure WAV format
    // TODO #4
    if (check_format(header) == false)
    {
        printf("Only works with wav files. Sorry!\n");
    }
    // Open output file for writing
    // TODO #5
    FILE *new_file = fopen(argv[2], "a");
    if (new_file == NULL)
    {
        printf("Could not open original file.\n");
        return 1;
    }
    // Write header to file
    // TODO #6
    fwrite(header.chunkID, 1, 4, new_file);
    //1 dword
    fwrite(&header.chunkSize, 4, 1, new_file);
    //4 bytes
    fwrite(header.format, 1, 4, new_file);
    //4 bytes
    fwrite(header.subchunk1ID, 1, 4, new_file);
    //1 dword
    fwrite(&header.subchunk1Size, 4, 1, new_file);
    //1 word
    fwrite(&header.audioFormat, 2, 1, new_file);
    //1 word
    fwrite(&header.numChannels, 2, 1, new_file);
    //1 dword
    fwrite(&header.sampleRate, 4, 1, new_file);
    //1 dword
    fwrite(&header.byteRate, 4, 1, new_file);
    //1 word
    fwrite(&header.blockAlign, 2, 1, new_file);
    //1 word
    fwrite(&header.bitsPerSample, 2, 1, new_file);
    //4 bytes
    fwrite(header.subchunk2ID, 1, 4, new_file);
    //1 dword
    fwrite(&header.subchunk2Size, 4, 1, new_file);
    // Use get_block_size to calculate size of block
    // TODO #7
    int block_size = get_block_size(header);

    // Write reversed audio to file
    // TODO #8
    BYTE buffer[block_size];
    while (fread(buffer, block_size, 1, raw_file) == 1)
    {
        fseek(new_file, 44, SEEK_SET);
        fwrite(buffer, block_size, 1, new_file);
    }
    fclose(raw_file);
    fclose(new_file);
}

bool check_format(WAVHEADER header)
{
    // TODO #4
    if (header.format[0] == 'W' && header.format[1] == 'A' && header.format[2] == 'V' && header.format[3] == 'E')
    {
        return true;
    }
    return false;
}

int get_block_size(WAVHEADER header)
{
    // TODO #7
    return header.numChannels * (header.bitsPerSample / 8);
}

WAVHEADER结构体定义

#include <stdint.h>

typedef uint8_t   BYTE;
typedef uint16_t  WORD;
typedef uint32_t  DWORD;

typedef struct
{
    BYTE   chunkID[4];
    DWORD  chunkSize;
    BYTE   format[4];
    BYTE   subchunk1ID[4];
    DWORD  subchunk1Size;
    WORD   audioFormat;
    WORD   numChannels;
    DWORD  sampleRate;
    DWORD  byteRate;
    WORD   blockAlign;
    WORD   bitsPerSample;
    BYTE   subchunk2ID[4];
    DWORD  subchunk2Size;
} __attribute__((__packed__))
WAVHEADER;

注:此为CS50x第四套习题(Pset4)中的Reverse题目。

错误分析与修复方案

1. 文件打开模式错误

  • 输入文件需用二进制读取模式"rb"打开,WAV是二进制文件,文本模式"r"会自动转换换行符,破坏二进制数据结构。
  • 输出文件需用二进制写模式"wb"打开,追加模式"a"会保留原有内容且无法正确定位写入,导致文件损坏;二进制模式才能保证写入数据的完整性。

2. WAV格式检查后未退出程序

当check_format返回false时,仅打印提示但继续执行后续代码,会对非WAV文件进行错误操作,需添加return 1;终止程序。

3. 输出文件打开错误提示混淆

打开输出文件失败时,提示文本错误指向原始文件,需修改为"Could not open output file."。

4. TODO 8反转逻辑完全错误

当前代码每次读取音频块后,都将输出文件指针移到44字节处覆盖写入,最终输出文件仅保留最后一个音频块,完全没有反转效果。正确的反转逻辑是从输入文件末尾向前逐块读取,依次写入输出文件:

// 修正后的TODO 8代码
BYTE buffer[block_size];
// 定位到输入文件音频数据的最后一个块起始位置
fseek(raw_file, -block_size, SEEK_END);
while (ftell(raw_file) >= 44) {
    fread(buffer, block_size, 1, raw_file);
    fwrite(buffer, block_size, 1, new_file);
    // 向前移动两个块的位置(因为读完后指针会向后移动一个块)
    fseek(raw_file, -2 * block_size, SEEK_CUR);
}

修复后的关键代码片段

// TODO #2 修正输入文件打开模式
FILE *raw_file = fopen(argv[1], "rb");
if (raw_file == NULL)
{
    printf("Could not open original file.\n");
    return 1;
}

// TODO #4 修正检查逻辑,添加退出
if (check_format(header) == false)
{
    printf("Only works with wav files. Sorry!\n");
    fclose(raw_file);
    return 1;
}

// TODO #5 修正输出文件打开模式和错误提示
FILE *new_file = fopen(argv[2], "wb");
if (new_file == NULL)
{
    printf("Could not open output file.\n");
    fclose(raw_file);
    return 1;
}

// TODO #8 修正反转逻辑
BYTE buffer[block_size];
// 定位到输入文件音频数据的最后一个块起始位置
fseek(raw_file, -block_size, SEEK_END);
while (ftell(raw_file) >= 44) {
    fread(buffer, block_size, 1, raw_file);
    fwrite(buffer, block_size, 1, new_file);
    // 向前移动两个块的位置(因为读完后指针会向后移动一个块)
    fseek(raw_file, -2 * block_size, SEEK_CUR);
}

内容的提问来源于stack exchange,提问作者Harr

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 11:08:10