将结构压缩为二进制文件?[C]

Bil*_*lie 2 c compression binaryfiles

这是我作业的一部分,我很难解决.

我有一个简单的结构:

typedef struct Client {
    char* lname;
    unsigned int id;
    unsigned int car_id;
} Client;
Run Code Online (Sandbox Code Playgroud)

演习是:

  1. 创建一个名为公司名称的文本文件,然后创建带有txt扩展名的分支编号.该文件包含所有客户的详细信息.

  2. 您在练习1中创建的文件将被压缩.因此,使用.cmpr扩展创建二进制文件.

我真的不知道如何实施2.

我记得在讲座中教授说我们必须使用"全部"变量,使用二元运算符(<<,>>,|,&,〜),但我不知道如何使用它.

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

我很乐意得到帮助.谢谢!

mea*_*ers 5

假设步骤1中的文件如下所示:

user1798362
2324
462345
Run Code Online (Sandbox Code Playgroud)

三个字段简单地印在三条线上.请注意,上面是该文件的文本/可读(即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                                
Run Code Online (Sandbox Code Playgroud)

nl当然是换行符.你可以算出有24个字节.

在第2步中,您必须创建另一种格式,以尽可能多地保存位.最简单的方法是分别压缩三个字段中的每一个.

类似于文本格式使用a nl来标记字段结尾的位置,您还需要一种方法来定义二进制字段的开始和结束位置.一种常见的方法是在二进制字段数据前放置一个长度.作为第一步,我们可以用nl长度替换's并得到:

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                                
Run Code Online (Sandbox Code Playgroud)

现在我们只需要一个字节长度的整个字节.注意,58是十六进制表示77(即11个字符*8位),位长lname',20 hex equals 4 * 8 = 32, and30 is 6 * 8 = 48. This does not compress anything, as it's still 24 bytes in total. But we already got a binary format because58 ,20 and30`有特殊含义.

下一步是压缩每个字段.这是它变得棘手的地方.该lname字段由ASCII字符组成.在ASCII中,只需要/使用8位中的7位; 这是一个很好的表例如u二进制的字母是01110101.我们可以安全地切断最左边的位,这总是如此0.这产生了1110101.所有角色都可以这样做.所以你最终得到11个7位值 - > 77位.

这77位现在必须适合8位字节.这里是user二进制表示中的前4个字节,在关闭最左边的位之前:

01110101 01110011 01100101 01110010
Run Code Online (Sandbox Code Playgroud)

通过将字节(即unsigned char)向左移位来完成C中的一些切换:

 unsigned char byte = lname[0];
 byte = byte << 1;
Run Code Online (Sandbox Code Playgroud)

当你为所有角色执行此操作时:

1110101- 1110011- 1100101- 1110010-
Run Code Online (Sandbox Code Playgroud)

这里我-用来表示这些字节中现在可以填充的位; 通过将所有位向左移动一位,它们变得可用.现在,您可以使用下一个字节右侧的一个或多个位来填充这些-间隙.为这四个字节执行此操作时,您将获得:

11101011 11001111 00101111 0010----
Run Code Online (Sandbox Code Playgroud)

所以现在有一个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;
Run Code Online (Sandbox Code Playgroud)

随着name[1] >> 7我们有1110011- >> 7这给:00000001; 最右边的一点.使用按位OR运算符,|我们然后"添加"此位1110101-,从而产生111010111.

你必须在循环中做这样的事情来获得正确字节中的所有位.

这个名称字段的新长度是11*7 = 77,所以我们丢失了大量的11位:-)请注意,对于字节长度,我们假设该lname字段永远不会超过255/7 = 36个字符长.

与上面的字节一样,您可以将第二个长度与lname字段的最后一位合并.

要压缩数字,首先要在(fscanf(file, %d, ...))中使用()读取它们unsigned int.0在这个4字节的unsigned int中,左侧会有很多s.例如,第一个字段是(仅为了可读性而以4位的块显示):

0000 0000 0000 0000 0000 1001 0001 0100
Run Code Online (Sandbox Code Playgroud)

左边有20个未使用的位.

你需要摆脱这些.32减去左边的零的数量,你得到这个数字的位长.bytes通过将其位与前一个字段的位相结合,将此长度添加到数组中.然后只将数字的有效位添加到bytes.这将是:

1001 0001 0100
Run Code Online (Sandbox Code Playgroud)

在C中,当使用'int'的位(但也是'short','long',......任何大于1个字节的变量/数字)时,必须考虑字节顺序或字节顺序.

对两个数字执行上述步骤两次时,您就完成了.然后,您有一个bytes可以写入文件的数组.当然,你必须bytes按照上面的步骤保留你所写的地方; 所以你知道字节数.请注意,在大多数情况下,最后一个字节中会有一些未填充数据的位.但这并没有伤害,它只是不可避免地浪费了这样一个事实,即文件最少以8位= 1字节的形式存储.

读取二进制文件时,您将获得一个反向过程.你会读一个unsigned char bytes数组.然后,您知道第一个字节(即bytes[0])包含name字段的位长.然后通过移位和屏蔽逐字节填写'lname'的字节.等等....

祝好运!