2013-10-15 45 views
0

爲了學習文件結構,我試圖讀取一個.wav文件並只是打印有關它的信息。我有一個持有定義爲這樣的所有信息的結構:爲什麼printf(「%s」,charstr)對每個fread()的打印效果都超過預期?

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字符串來打印長。下面是一個示例代碼段:

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");)不同?文件規格要求成員的大小具有在我的struct中定義的確切大小。做char chunkId[5];,然後chunkId[4]='\0';似乎不是這個問題的一個很好的解決方案。

我一直在試圖解決這個問題已經有幾天了,所以現在我來到這裏也許會朝着正確的方向前進。

爲了全面披露,這裏是文件相關部分的十六進制輸出,因爲這個webform並沒有顯示出現在輸出中的所有亂碼。

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 
+0

這就是它的一部分,但我也試圖理解爲什麼在這個例子中'fread'和'printf'相關聯。理論上,如果我讀完整個文件,printf會嘗試顯示文件的全部79Mb。 –

+0

(缺少其他格式化程序),printf將一直運行,直至找到0字節。在79 MB數據中找不到0字節是非常不可能的(但並非不可能)! – abelenky

+0

所以我猜字符串文字,編譯器在程序集中添加了空終止符? –

回答

2

如果你知道的大小,可以限制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); 
2

您調用printf()的方式,它期望一個'\0'終止字符串,但您的結構元素不是(fread()不會添加'\0'format,chunkId等沒有足夠的長度來包含它)。

最簡單的方法是:

printf("%.*s\n", (int)sizeof(wavFile.format), wavFile.format); 
+0

@AndreyT,你說的沒錯。謝謝。 –

1

如果它不是一個空值終止字符串可以使用.*和一個額外的int參數,該參數指定字符串的大小printf,例如:

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

或者:

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

在你的情況下可能會更簡單,因爲尺寸似乎在你的情況下是固定的。

的printf文檔中的格式串的精度符以上的工作方式如下:

(可選)。後面跟着整數或*表示轉換的精度。在使用*的情況下,精度由類型爲int的附加參數指定。如果這個參數的值是負數,它將被忽略。請參閱下表以瞭解精確度的確切影響。

和低於該文本此引用說,以下爲字符串表:

精度指定要寫入的字節的最大數量。

1

首先,確保你在讀二進制模式(使用fopen設置爲"rb"模式)的文件。這對類Unix系統沒有任何影響,但其他人在文本模式下讀取二進制文件可能會導致數據損壞。你應該檢查每個fread()呼叫返回的值;不要只假定一切正常。

printf%s格式需要指向字符串的指針。 A 字符串始終有一個空字符'\0'來標記它的結尾。

如果您從文件中讀取大量數據,則不太可能具有終止空字符。

正如其他答案所說,%s格式的變體可以限制打印字符的數量,但即使如此,printf也不會打印出發生在數組中的第一個空字符之後的任何內容。 (A空字符,這是簡單地用值0字節,可能是有效的數據,並在其後,可以有更多的有效數據。)

要打印已知長度的任意的字符數據,使用fwrite

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

在這種情況下,看起來您期望chunkId包含可打印的字符;在你的例子中,它有"RIFF"(但沒有結尾的空字符)。但你可能正在讀取一個無效的文件。

將二進制數據打印到標準輸出可能會有問題。如果碰巧包含可打印的字符,那很好,並且您可以假設所有內容在最初版本中都是可打印的。但是您可能會考慮檢查陣列中的字符是否可以打印(請參見isprint()),如果不是,則以十六進制打印它們的值。

相關問題