C语言WAV文件反转程序异常:输出文件损坏无法播放
我使用C语言编写了一个WAV文件反转程序,已完成编写并多次检查逻辑错误,程序可编译生成可执行文件并输出WAV文件,但输出文件存在损坏/错误,无法播放音频。请帮忙排查代码问题,感谢。
#include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include "wav.h" int check_format(WAVHEADER header); int get_block_size(WAVHEADER header); int main(int argc, char *argv[]) { // Ensure proper usage if (argc != 3) { printf("usage: ./reverse input.wav output.wav\n"); return 1; } // Open input file for reading char *input_name = argv[1]; char *output_name = argv[2]; FILE *inptr = fopen(input_name, "r"); if (inptr == NULL) { printf("File could not be opened\n"); return 1; } // Read header WAVHEADER in_wav_header; fread(&in_wav_header, sizeof(WAVHEADER), 1, inptr); // check if input file is WAV file if (check_format(in_wav_header) != 0) { fclose(inptr); printf("unsupported file format\n"); return 1; } // Open output file for writing FILE *outptr; outptr = fopen(output_name, "w"); if (outptr == NULL) { printf("error creating output file\n"); return 1; } // Write header to file fwrite(&in_wav_header, sizeof(WAVHEADER), 1, inptr); // Use get_block_size to calculate size of block int block_size = get_block_size(in_wav_header); printf("%i\n", block_size); // declare buffer array BYTE *buffer = malloc(sizeof(block_size)); // set cursor to start of last block of input audio file fseek(inptr, -block_size, SEEK_END); // iterate through input file blocks, checking for header, and writing blocks // to the output file while (ftell(inptr) >= sizeof(WAVHEADER)) { fread(buffer, block_size, 1, inptr); fwrite(buffer, block_size, 1, outptr); if (fseek(inptr, 2 * -block_size, SEEK_CUR)) { return 1; } } // close files & free malloc space free(buffer); fclose(inptr); fclose(outptr); } int check_format(WAVHEADER header) { char check[] = {'W', 'A', 'V', 'E'}; for (int i = 0; i < 4; i++) { if (header.format[i] != check[i]) { return 1; } } return 0; } int get_block_size(WAVHEADER header) { if (header.numChannels == 1 || header.numChannels == 2) { int block_size = (header.bitsPerSample / 8) * header.numChannels; return block_size; } else { printf("error: too many audio channels\n"); return 1; } }
代码中的关键错误及修复方案
头文件写入目标错误:代码中
fwrite(&in_wav_header, sizeof(WAVHEADER), 1, inptr);将WAV头写入了输入文件inptr,而非输出文件outptr。这会破坏原输入文件,同时输出文件缺少必要的WAV头,直接导致文件无法被音频播放器识别。
修复:将inptr改为outptr:fwrite(&in_wav_header, sizeof(WAVHEADER), 1, outptr);二进制文件打开模式错误:WAV是二进制文件,使用
"r"和"w"模式打开会触发系统自动转换换行符(Windows系统下),破坏二进制数据的完整性。
修复:打开文件时使用二进制模式:FILE *inptr = fopen(input_name, "rb"); FILE *outptr = fopen(output_name, "wb");内存分配大小错误:
malloc(sizeof(block_size))实际分配的是int类型的字节数(通常为4字节),而非block_size指定的音频块大小。若block_size大于4,会导致缓冲区溢出,写入错误数据。
修复:直接使用block_size作为内存分配的大小:BYTE *buffer = malloc(block_size);错误返回值混淆:
get_block_size函数在通道数非法时返回1,但1是有效的音频块大小(比如单声道8位音频),会导致后续逻辑误以为块大小有效,继续执行错误操作。
修复:返回负数作为错误标识:else { printf("error: too many audio channels\n"); return -1; }同时在主函数中增加错误判断:
int block_size = get_block_size(in_wav_header); if (block_size <= 0) { fclose(inptr); fclose(outptr); return 1; }循环边界潜在问题:当音频数据长度刚好是块大小的整数倍时,最后一次
fseek可能会让文件指针跳到WAV头之前,导致多读无关数据。可以调整循环逻辑,确保只处理音频数据部分:// 计算音频数据起始位置和总长度 long data_start = sizeof(WAVHEADER); long current_pos = ftell(inptr); while (current_pos >= data_start) { fread(buffer, block_size, 1, inptr); fwrite(buffer, block_size, 1, outptr); current_pos -= 2 * block_size; if (fseek(inptr, current_pos, SEEK_SET) != 0) { break; } }
内容的提问来源于stack exchange,提问作者Charlie Webster

