比方说,从步骤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