0

为了学习文件结构,我尝试读取 .wav 文件并简单地打印有关它的信息。我有一个结构,其中包含定义为的所有信息:

typedef struct{
   char chunkId[4];
   unsigned int chunkSize;
   char format[4];
   char subchunk1Id[4];
   unsigned int subchunk1Size;
   unsigned short audioFormat;
   unsigned short numChannels;
   unsigned int sampleRate;
   unsigned int byteRate;
   unsigned short blockAlign;
   unsigned short bitsPerSample;
   char subchunk2Id[4];
   unsigned int subchunk2Size;
   void *data;
} WavFile;

发生的事情是,每次我fread通过文件时,都会导致我的 c-strings 打印得越来越长。这是一个示例代码片段:

   fseek(file, SEEK_SET, 0);
   fread(wavFile.chunkId, 1, sizeof(wavFile.chunkId), file);
   fread(&wavFile.chunkSize, 1, sizeof(wavFile.chunkSize), file);
   fread(wavFile.format, 1,sizeof(wavFile.format), file);  
   fread(wavFile.subchunk1Id, 1, sizeof(wavFile.subchunk1Id), file);
   fread(&wavFile.subchunk1Size, 1, sizeof(wavFile.subchunk1Size), file);
   fread(&wavFile.audioFormat, 1, sizeof(wavFile.audioFormat), file);

   printf("%s\n",wavFile.chunkId);
   printf("%d\n",wavFile.chunkSize);
   printf("%s\n",wavFile.format);
   printf("%s\n",wavFile.subchunk1Id);
   printf("%d\n",wavFile.subchunk1Size);
   printf("%d\n",wavFile.audioFormat);

我的struct设置方式、读取文件的方式或printf()查看字符串的方式导致输出打印如下所示:

RIFF�WAVEfmt 
79174602
WAVEfmt 
fmt 
16
1

预期输出:

RIFF
79174602
WAVE
fmt
16
1

我确实知道 c 字符串需要以空值结尾,但后来我开始思考从二进制文件打印字符串与打印字符串文字有何不同,例如printf("test"); 文件规范要求成员的大小具有在 my 中定义的确切大小struct。做char chunkId[5];然后chunkId[4]='\0';似乎不是解决这个问题的好方法。

几天来我一直在尝试解决这个问题,所以现在我来到 SO 可能会朝着正确的方向前进。

为了全面披露,这里是文件相关部分的十六进制输出,因为这个网络表单没有显示我的输出中出现的所有乱码。

52 49 46 46 CA 1B B8 04 57 41 56 45 66 6D 74 20 10 00 00 00 01 00 02 00 44 AC 00 00 98 09 04 00 06 00 18 00 64 61 74 61
4

4 回答 4

3

如果你知道大小,你可以限制输出printf

// Only prints 4-bytes from format.  No NULL-terminator needed.
printf("%.4s\n", wavFile.format);

如果大小存储在不同的字段中,您也可以使用它:

// The * says: print number of chars, as dictated by "theSize"
printf("%.*s\n", wavFile.theSize, wavFile.format);  
于 2013-10-15T13:10:49.087 回答
2

您调用的方式printf(),它需要一个'\0'终止的字符串,但您的结构元素不是(fread()不添加'\0'andformatchunkId没有足够的长度来包含它)。

最简单的方法是:

 printf( "%.*s\n", (int)sizeof(wavFile.format), wavFile.format );
于 2013-10-15T13:11:42.340 回答
1

如果它不是一个以null 结尾的字符串,您可以使用.*一个额外的int参数来指定printf字符串的大小,例如:

 printf("%.*s\n", (int)sizeof(wavFile.chunkId), wavFile.chunkId);

或者:

 printf("%.4s\n", wavFile.chunkId);

在您的情况下,这可能更简单,因为您的情况似乎是固定的。

从上面的printf文档中,格式字符串中的精度说明符的工作方式如下:

(可选的) 。后跟指定转换精度的整数或 *。在使用 * 的情况下,精度由 int 类型的附加参数指定。如果此参数的值为负,则将其忽略。有关精度的确切影响,请参见下表。

并且本文引用的下表对字符串说明了以下内容:

精度指定要写入的最大字节数。

于 2013-10-15T13:11:54.257 回答
1

首先,确保您以二进制模式读取文件(使用fopen模式设置为"rb")。这在类 Unix 系统上没有区别,但在其他系统上以文本模式读取二进制文件可能会给您损坏的数据。你应该检查每次fread()调用返回的值;不要只是假设一切正常。

printf使用%s格式需要指向字符串的指针。字符串总是有一个空字符来标记它的结尾'\0'

如果您从文件中读取大量数据,则不太可能有终止空字符。

正如其他答案所说,%s格式的变化可以限制打印的字符数,但即便如此,printf也不会打印超出数组中出现的第一个空字符的任何内容。(一个空字符,就是一个值为0的简单字节,可能是有效数据,后面可能还有更多有效数据。)

要打印已知长度的任意字符数据,请使用fwrite

fwrite(wavFile.chunkId, sizeof wavFile.chunkId, 1, stdout);
putchar('\n');

在这种特殊情况下,您似乎希望chunkId包含可打印的字符;在您的示例中,它具有"RIFF"(但没有尾随空字符)。但您可能正在读取无效文件。

将二进制数据打印到标准输出可能会出现问题。如果它恰好包含可打印的字符,那很好,您可以假设所有内容在初始版本中都是可打印的。但是您可能会考虑检查数组中的字符是否实际上是可打印的(请参阅 参考资料isprint()),如果不是,则以十六进制打印它们的值。

于 2013-10-15T15:16:47.050 回答