2013-06-21 48 views
1

這是我難以解決的作業的一部分。將結構壓縮成二進制文件? [C]

我有一個簡單的結構:

typedef struct Client { 
    char* lname; 
    unsigned int id; 
    unsigned int car_id; 
} Client; 

而且鍛鍊是:

  1. 創建一個名爲作爲公司名稱的文本文件,然後分枝數以txt推廣。 該文件包含所有客戶的詳細信息。

  2. 您在練習1中創建的文件將被壓縮。因此,使用.cmpr擴展名創建二進制文件。

我真的沒有一個想法如何落實2

我記得在那個教授說,我們必須使用「全部」的變量,用二元運算符的演講(< <,>>,|,&,〜),但我不知道如何使用它。

我使用的是GCC和Eclipse下的Ubuntu。我正在使用C.

我很樂意獲得幫助。謝謝!

+1

那麼,對於壓縮,我會使用libzip或至少libz ...你是什麼意思「不得不使用所有變量」? – 2013-06-21 08:15:35

+0

@ H2CO3例如,如果我使用'int',但僅使用5位,則將下一個變量壓縮到剩下的3箇中 – Billie

+0

@userXXX什麼是「下一個變量」?左3 ...但是什麼? – 2013-06-21 08:18:01

回答

5

比方說,從步驟1中的文件看起來像:

​​

其中三個領域簡單地印上三行。請注意,以上是該文件的文本/可讀(即ASCII)表示。

望着這個文件的十六進制內容(adecimal)表示,我們得到(印有下面的每個字節的值的ASCII字符):

75 73 65 72 31 37 39 38 33 36 32 0a 32 33 32 34 0a 34 36 32 33 34 35 0a 
u s e r 1 7 9 8 3 6 2 nl 2 3 2 4 nl 4 6 2 3 4 5 nl         

這裏nl是當然的換行符。你可以指望有24個字節。

在第2步中,您必須發明另一種可以節省儘可能多的位的格式。最簡單的方法是分別壓縮三個字段中的每一個。

與文本格式使用nl標記字段結尾的位置類似,您還需要一種方法來定義二進制字段開始和結束的位置。一種常用的方法是在二進制字段數據前加上一個長度。作爲第一步,我們可以用一個長度替換nl的,並得到:

58 75 73 65 72 31 37 39 38 33 36 32 20 32 33 32 34 30 34 36 32 33 34 35 
-- u s e r 1 7 9 8 3 6 2 -- 2 3 2 4 -- 4 6 2 3 4 5         

現在我們只是需要一個整體字節的比特長度。請注意,58是77的十六進制表示(即11個字符* 8位),位長爲 30`具有特殊含義。

下一步將壓縮每個字段。這是棘手的地方。 lname字段由ASCII字符組成。在ASCII中,只需要使用8位中的7位; here's a nice table例如,二進制中的字母u01110101。我們可以安全地切掉最左​​邊的位,這總是0。這產生了1110101。所有角色都可以做到這一點。所以你會得到11個7位值 - > 77位。

這些77位現在必須適合8位字節。以下是第一個4個字節user在二進制表示,切碎最左邊的位關閉之前:

unsigned char byte = lname[0]; 
byte = byte << 1; 

01110101 01110011 01100101 01110010 

斬去在C位由字節(即unsigned char)向左移位用做

當你這樣做的所有字符你:

1110101- 1110011- 1100101- 1110010- 

這裏我用-以表示這些字節的比特現在可以填補;它們通過將所有位移到一個位置而變得可用。您現在使用下一個字節右側的一個或多個位來填補這些空白。做這件事時這四個字節,你會得到:

11101011 11001111 00101111 0010---- 

所以現在有4位認爲應該充滿從角色1位,填補了這些空白差距等

是通過使用你提到的C中的二元運算符來完成。我們已經使用左移<<。要結合例如1110101-1110011-我們這樣做:

unsigned char* name; // name MUST be unsigned to avoid problems with binary operators. 
<allocated memory for name and read it from text file> 

unsigned char bytes[10]; // 10 is just a random size that gives us enough space. 

name[0] = name[0] << 1; // We shift to the left in-place here, so `name` is overwritten. 
name[1] = name[1] << 1; // idem. 

bytes[0] = name[0] | (name[1] >> 7); 
bytes[1] = name[1] << 1; 

隨着name[1] >> 7我們1110011- >> 7這給:00000001;最右邊的位。使用按位或運算符|,然後我們將該位添加到1110101-,導致111010111

你必須在循環中做這樣的事情,以獲得正確字節中的所有位。

這個名稱字段的新長度是11 * 7 = 77,所以我們失去了一個巨大的11位:-)請注意,對於字節長度,我們假設lname字段永遠不會超過255/7 = 36個字符。

與上面的字節一樣,您可以合併第二個長度與lname字段的最後一位。

要壓縮數字,您首先在unsigned int中讀取'(em)(fscanf(file, %d, ...))。在這個4字節無符號整數的左邊將會有很多0 s。例如第一字段(示出在僅是爲了便於閱讀4位的組塊):

0000 0000 0000 0000 0000 1001 0001 0100 

其具有在左側20未使用的位。

你需要擺脫這些。做32減去零的數字在左邊,你得到這個數字的位長。將此長度與bytes陣列的位相加,並將其與以前的字段合併。然後只將該數字的有效位添加到bytes。這將是:

1001 0001 0100 

在C,有「廉政」的位工作時(也是「短」,「長」,......任何變量/數大於1個字節大),您必須考慮字節順序或endianness

當您對兩個數字執行上述步驟兩次時,就完成了。然後你可以寫一個bytes數組寫入文件。當然你必須保留你在bytes上面的步驟中寫的地方;所以你知道字節數。請注意,在大多數情況下,最後一個字節中會有幾位未填充數據。但是這並沒有什麼壞處,並且它簡單地避免了文件以最少8位= 1字節的形式存儲的事實。

讀取二進制文件時,您會得到一個反向過程。您將閱讀unsigned charbytes數組。然後您知道第一個字節(即bytes[0])包含名稱字段的位長。然後通過移位和掩碼來填寫'lname'字節的字節。 etc ....

祝你好運!