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)
演习是:
创建一个名为公司名称的文本文件,然后创建带有txt扩展名的分支编号.该文件包含所有客户的详细信息.
您在练习1中创建的文件将被压缩.因此,使用.cmpr扩展创建二进制文件.
我真的不知道如何实施2.
我记得在讲座中教授说我们必须使用"全部"变量,使用二元运算符(<<,>>,|,&,〜),但我不知道如何使用它.
我在GCC和Eclipse下使用Ubuntu.我正在使用C.
我很乐意得到帮助.谢谢!
假设步骤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'的字节.等等....
祝好运!