比方說,從步驟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例如,二進制中的字母u
是01110101
。我們可以安全地切掉最左邊的位,這總是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 char
bytes
數組。然後您知道第一個字節(即bytes[0]
)包含名稱字段的位長。然後通過移位和掩碼來填寫'lname'字節的字節。 etc ....
祝你好運!
那麼,對於壓縮,我會使用libzip或至少libz ...你是什麼意思「不得不使用所有變量」? – 2013-06-21 08:15:35
@ H2CO3例如,如果我使用'int',但僅使用5位,則將下一個變量壓縮到剩下的3箇中 – Billie
@userXXX什麼是「下一個變量」?左3 ...但是什麼? – 2013-06-21 08:18:01