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
相关产品推荐
相关产品推荐

