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

时间:2013-06-21 08:13:01

标签: c compression binaryfiles

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

我的结构很简单:

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

练习是:

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

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

  4.   

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

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

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

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

1 个答案:

答案 0 :(得分:6)

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

user1798362
2324
462345

这三个字段只是打印在三行上。请注意,上面是该文件的文本/可读(即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位),位长lname', 20 hex equals 4 * 8 = 32, and 30 is 6 * 8 = 48. This does not compress anything, as it's still 24 bytes in total. But we already got a binary format because 58 , 20 and 30`有特殊意义。

下一步是压缩每个字段。这是它变得棘手的地方。 lname字段由ASCII字符组成。在ASCII中,只需要/使用8位中的7位; here's a nice table例如,二进制中的u字母为01110101。我们可以安全地砍掉最左边的位,它总是0。这会产生1110101。所有角色都可以这样做。所以你最终得到11个7位值 - > 77位。

这77位现在必须适合8位字节。这里是二进制表示中的前4个字节user,然后将最左边的位切掉:

01110101 01110011 01100101 01110010

通过将字节(即unsigned char)向左移动来完成在C中删除一点:

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

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

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;最右边的一点。使用按位OR运算符|,我们将此位“添加”到1110101-,结果为111010111

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

此名称字段的新长度为11 * 7 = 77,因此我们丢失了大量的11位:-)请注意,对于字节长度,我们假设lname字段永远不会更多超过255/7 = 36个字符。

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

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

0000 0000 0000 0000 0000 1001 0001 0100

左边有20个未使用的位。

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

1001 0001 0100

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

当您对这两个数字执行上述步骤两次时,您就完成了。然后,您可以使用bytes数组写入文件。当然,在上述步骤中,您必须保持在bytes的写作位置;所以你知道字节数。请注意,在大多数情况下,最后一个字节中会有一些未填充数据的位。但这并没有什么坏处,只是不可避免地浪费了文件以8位= 1字节的最小块存储的事实。

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

祝你好运!