这是我作业的一部分,我很难解决。
我的结构很简单:
typedef struct Client {
char* lname;
unsigned int id;
unsigned int car_id;
} Client;
练习是:
创建一个名为公司名称的文本文件,然后创建带有txt扩展名的分支编号。 该文件包含所有客户的详细信息。
- 醇>
您将在练习1中创建的文件将被压缩。因此,可以使用.cmpr扩展名创建二进制文件。
我真的不知道如何实施2。
我记得在讲座中教授说我们必须使用“全部”变量,使用二元运算符(<<<<<<<>,>,|,&,〜),但我不知道如何使用它。
我在GCC和Eclipse下使用Ubuntu。我正在使用C.
我很乐意得到帮助。谢谢!
答案 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'的字节。等....