复合文档格式研究之01-数据存储简介

 

前言

去年,我在某论坛看到有大牛破解VBA项目工程的帖子,却没有公布任何技术细节。对于好奇心天生就强的我当然要追根问底,于是在微软的官方网站下载了许多技术文档研究,也参考了许多人写的博客,结合自己的实践,理解了复合文档格式。

其实,对于一个完全没有接触过复合文档格式的人来说,即使你找到了网上各个现成的资料,恐怕仍然很难理解透彻。即使是微软官方提供的解析复合文档的工具,也存在一个致命的错误。所以,我决定还是花些时间写下这篇,不是为了教大家破解VBA项目,而是希望大家能够通过学习,理解复合文档格式,从而对计算机存储方式有一个初步的了解。从个人角度来看,尽管复合文档格式目前正在被逐渐取代,但不能否认它的确是一个很了不起的发明。

本文从原理入手,帮助大家理解复合文档格式的基本概念。并随后用一个示例,使用VBA代码实现复合文档的解析,帮助进一步掌握复合文档格式。

 

1、计算机中数据的存储

1.1 计算机存储

当你阅读这篇文章的时候,你是否想过这篇文章在计算机内是如何存储的呢?阅读完本章的内容,相信你一定对于存储以及存储的思维有一定的了解。

我们知道,计算机中的数据,最小的数据存储的计算单位是字节。如果我们把一个字节画成一个小格子,那么你可以认为数据是存储在一个个小格子里面的。


图1         计算机内存示例

 

我们假设建立一个Word文档,其中填写“你好,这是第一篇文章”几个字,然后存储。计算机中的内存示例如下。(此处仅为示例,实际存储并非与本示例相同)


图2         第一次添加内容后的存储示例

 

然后我们再次打开,在文档中添加几个字“你好,这是第二篇文章”,之后保存。计算机中的内存示例如下。

 

图3         第二次添加内容后的存储示例

 

然后我们再次打开,在第一段文字和第二段文字中间插入一段文字“第一篇文章有点扯淡。”,再次保存。

 

图4         第三次插入内容后的存储示例

 

神奇的事情发生了!计算机中存储并非如按照文档实际的数据的顺序存储,计算机的存储逻辑就是,只要找到空的未使用的空间就把数据放进去。为什么要这样设计呢?主要有两个原因:

  1. 减少数据读写次数。

  2. 充分利用空间

举个例子就能大概明白了。

 

1.2 顺序存储的缺点

如果我们按照文档顺序存储,那么将会经历几个过程:

    1、将插入点后的数据预留出空间,写入到之后的单元格中

 

 

    2、将所要插入的文字写入

 

3、如果恰好第二句之后的空间被其他数据占用,那么程序需要找到新的空闲存储空间,并且该空间要足够大,可以用以存放完整的数据。

 

 

如果经历了上述步骤之后,就会面临几个比较严重的问题:

  • 读写磁盘导致存储变慢(如果这篇文章有几十兆,而你在最开头插入一段话,那么就会读取和存储另几十兆的内容)

  • 如果连续的空闲空间不足以存放完整的数据,就会导致数据无法存储

 

1.3 索引表

为了避免上述的问题(尤其是问题2将导致无法存储数据),计算机中的数据都是零散化存储(如图4所示),当找到空闲空间就可以存放数据。那么问题来了:计算机中存储的数据与实际数据逻辑顺序并不一致,在打开文件的时候,计算机如何知道实际数据是怎样的呢?由于数据的打乱,就需要一个索引表(目录)来记录正确的数据顺序,这个索引表或者称为目录表的,其实也是一堆的数据,这个表一般都是按顺序存储,存放的就是真正数据的地址。

 

 9 索引表示意图

 

这个索引表和存储数据的都属于这个文档的组成部分。当打开这个文档的时候,首先读取的是索引表,然后根据索引表读取实际的文档内容,之后再呈现给使用者。

学习过DOS的朋友,一定接触过分区表,其实这里的索引表和分区表的实际意义是相同的。我们在Windows中进行的快速格式化,其实也只是将分区表重建,并不是真正删除其中的内容。

 

1.4 改进的索引表

增加了索引表后,我们牺牲了一些存储空间,但是换来了效率。说起效率,聪明的你一定发现了一个问题。即使建立了索引表,由于索引表仍然按照逻辑顺序存储,因而当有大量数据需要变换顺序或者执行插入等操作的时候,仍然没有改变需要重复读取和写入索引表的缺陷,只不过把需要读写的数据转移到了单纯的地址的读写。

因而,在一般的存储中,索引表中的一条完整的记录不仅仅存储实际数据的地址,还存放了逻辑顺序的下一条索引的地址。这听起来有些复杂,但其实很简单,却是非常有智慧的设计。我们先来看下存储的示意图,现在针对同一个数据的一条完整的索引存放的地址增加到了2条。

 

 10         改进的索引表

图7的红色箭头即表示获取地址的顺序,其中白色表示的内存中存放了下一个索引的地址,绿色表示的内存中存放了实际存放数据的地址。我们来解析一下我们如何通过这张索引表组合成实际的数据。

  • 从第一条索引记录开始,读取数据地址1所表示的数据,然后读取下一条索引的地址103

  • 读取103地址中的数据地址21所表示的数据,然后读取下一条索引的地址105

  • 读取105地址中的数据地址11所表示的数据,然后读取下一条索引地址FFF,这个地址是一个约定的结束标识,说明了这个文档的数据全部读取完毕

  • 看完后有没有疑问呢?如果有的话说明你已经理解了这种数据存储的思维了。如果没有,那么请再仔细看一下之前的章节,看看这个流程有没有什么遗漏?

 

1.5 文件头

有没有看出1.4中所描述的流程中的缺失部分呢?其实答案很简单,索引记录的地址103,105都在索引表中体现了出来,那么第一条索引的地址101如何获取呢?没有找到第一条索引,那么整个数据就无从找起。

因而,在每个文档的开头的地址,都预留了一部分的空间,用以存放这个文件的基本信息,其中就包括索引的起始地址。

这部分的信息就称为文件头。文件头的开始部分一般用一串代码表示文件的格式,然后是文件的各种信息,由于每种软件都有自己的文件格式系统,所以文件头的标准是各不一样的。在此,就不再赘述,在之后的章节我们会一起来解析一下xls的文件头。

1.6 扇区

还有一点需要提到的是,当我们记录数据地址的时候,如果每个字节都要记录,那显然不合理,那样的话你的硬盘根本村不了几个文件。因而,计算机在实际存储文件的时候,虽然字节是最小的存储单位,但是实际上存储空间有另一个最小单位,这个单位就称为扇区。

举个简单的例子来帮助理解一下。某饭馆大堂有10张桌子,每张桌子可以坐10个人。来了一家10个人,正好坐满了1号桌。又来了一家5个人,坐在了2号桌。接着又来了一家5个人,此时就不能再安排在2号桌了,谁也不愿意拼桌吧,所以只能安排在3号桌。

在这个例子中,座位和字节一样,是逻辑意义上的最小容纳单位,而桌子则和扇区一样,是实际意义的最小容纳单位。在饭店上菜的时候,只会记录桌号上菜,在计算机存储系统中,索引表也只会记录扇区的起始地址。计算机中的存储系统实际是以扇区划分的,一般都是512字节作为一个扇区。饭馆的例子中,有3个不超过10人的家庭,实际占用了3张桌子。而在计算机系统中也是一样,如果有3种不同功能的数据,那怕一种数据只有1个字节,也需要占用3个扇区即512*3个字节。你可以试着打开一个记事本,然后输入一些内容之后保存,我们用右键点击这个文件,然后查看一下属性看看。

 

 11         文件存储属性示意

看到了什么?文件大小只有1个字节,而占用磁盘用了4K。这个4K和我们分区的时候设置有关,实际上是8个扇区。事实上,所有的文件占用的空间都是512的整数倍,你可以试试看。这是由扇区决定的。

回到我们的文档,我们也是以512字节作为一个扇区存储数据,里面最多存放512个字节,当字节超过512时,就会占用512整数倍的存储空间。比如513个字节,就会占用2个扇区,也就是1KB的存储。

在复合文档格式的描述中,还有一个称为Mini Sector(短扇区)的空间,其大小为64个字节。两者结合来存放数据,可以更有效地利用存储空间。

1.7 小结

作为开头的第一章,讲了很多与我们复合文档没什么太大关系的内容,而且概念很多,希望大家能够反复阅读并消化。理解了上述概念,可以帮助我们快速理解复合文档格式。

 

文章出处:

 

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值