复合文档格式研究之05-DIFAT、FAT、MINIFAT

李懿 Excel学习 2016-07-05

微信扫一扫
关注该公众号

需要之前的文章,请在公众号内回复:

复合文档

 

5、读取DIFATFATMINIFAT

上一次,我们成功读取了文件头Header。这一次,我们要根据Header中的信息读取DIFAT,FAT和MINIFAT。这三个信息至关重要,它直接关系到我们数据真正的存储顺序。在那之前,先来复习并扩展一下这些基本的概念。

5.1 文件和扇区

是否还记得第一部分我们介绍的基础知识呢?数据存储是以扇区为单位的,对于文件也是一样。一个文件可能占用多个扇区。对于复合文档,每个不同类型的数据都是单独存放在不同扇区的,换句话说,一个扇区内只存放一种类型的数据。有些扇区存放FAT信息,有些存放DIFAT信息,有些则存放普通的文件的数据。


22         文件和扇区示例

如上图所示,除去文件头外,每个扇区从开始到最后都有一个从0开始的数字编号。若要读取某个扇区的信息,我们要获取扇区相对于文件开头的偏移量,可以采用以下的公式:

扇区ID x 扇区大小 + 512

其中512是文件头占用的空间,文件头之后的0号扇区的偏移量就是0*512+512=512,依此类推。

5.2  扇区ID

前文所述一个扇区只会存放一种类型的数据,因而在复合文档的FAT数据中,除了表示扇区ID的数字,还有些特殊的数字ID表示一些特定的扇区。


 23         扇区ID类型

为了方便使用,根据扇区类型,可以声明一些枚举值如下:

Public Enum SECTORTYPE
   MAXREGSECT = &HFFFFFFFA
   NASECT = &HFFFFFFFB   
   DIFSECT = &HFFFFFFFC   
   FATSECT = &HFFFFFFFD   
   ENDOFCHAIN = &HFFFFFFFE

   FREESECT = &HFFFFFFFF

End Enum

 

5.3 FAT

FAT中记录所有扇区的详细信息,包括扇区的类型,数据扇区的顺序等等。

可以把FAT当成一个数组,数组中存储的都是Long类型的整数(4个字节),则其中的每个整数代表一个扇区的ID,代表了数据区域下一个扇区的ID。我们用一个下标起始为0(与扇区ID同步)的数组来表示FAT,来看一个具体的示例。


 24         FAT示例

上图对应了之前的那个文件FAT[0]表示了0号扇区,这个扇区以及4、5号扇区存储了FAT的信息,因而其中存储了十六进制数0xFFFFFFFD。3、6号扇区为DIFAT扇区,因而其中存储了十六进制数0xFFFFFFFC。扇区1、2、N存放了正常的文件数据,因而其数字表示的是该扇区的下一个数据扇区的ID,比如与1号扇区连续的下一个数据扇区是2号,与2号扇区连续的下一个数据扇区是7号。而在N号扇区的FAT中存放了0xFFFFFFFE,表示这个扇区之后没有更多数据扇区。

通常一个扇区大小为512字节,因而一个FAT扇区中,最多可以存放128组FAT信息。在一个扇区内部,这些FAT的信息都是连续的,即FAT所表示的扇区都是连续的。比如某个FAT扇区,第1个FAT代表的扇区的ID是51的话,则这个扇区可以表示ID从51到178的扇区。

要把不同的FAT扇区串联起来的方法则是通过DIFAT列表。

5.4 DIFAT

DIFAT是FAT扇区的索引,表示了FAT扇区的前后顺序。DIFAT和FAT类似,也可以用数组表示,其存储值表示了FAT扇区的正确读取顺序。

除了Header中存储的109个DIFAT数据,还可以使用其它扇区专门存储DIFAT数据。作为DIFAT扇区,由于其可能有存在多个扇区组合,并且它没有额外的索引表示其顺序,因而其存储的数据不像FAT扇区那样全部都用于存储FAT数据。在DIFAT扇区的最后4个字节,存储了下个DIFAT扇区的ID,以表示下个DIFAT数据该去哪儿找。


图25         DIFAT扇区示意

5.5 使用VBA解析DIFAT

前109个DIFAT存储在Header之中,若DIFAT超过109个,则在Header中可以通过DIFATSectorStart读取第一个DIFAT扇区的ID,然后读取该扇区的127个DIFAT信息。最后一个Long类型的数据即为下一个DIFAT扇区的ID。

我们添加以下代码:

'读取DIFAT

Sub ReadDIFAT(FS As Integer, Hdr As CFBHeader, DIFAT() As Long, SectorSize As Long)

   Dim i   As Long

   '先读取Header中的DIFAT

   For i = 0 To 108

        '遇到结束ID,则跳出

        If Hdr.DIFAT(i) = SECTORTYPE.ENDOFCHAIN Or Hdr.DIFAT(i) = SECTORTYPE.FREESECT Then

            Exit Sub

        End If

        '添加DIFAT

        AddItem2List DIFAT, Hdr.DIFAT(i)

   Next i

   '若还有其他DIFAT扇区,则继续读取

   If Hdr.DIFATSectorCount > 0 Then _

        ReadDIFATSector FS, DIFAT,Hdr.DIFATSectorStart, SectorSize

End Sub

 

'读取指定扇区的DIFAT数据

Sub ReadDIFATSector(FS As Integer,DIFAT() As Long, SectorID As Long, SectorSize As Long)

   Dim i   As Long

   Dim ID  As Long

   '定位

   Seek FS, GetFileOffset(SectorID, SectorSize)

   '读取DIFAT。在一个DIFAT扇区中,最多有127个DIFAT数据,最后一个为下一个DIFAT扇区的ID

   For i = 0 To 126

        '读取数据

        Get FS, , ID

        '遇到结束ID,则跳出

        If ID = SECTORTYPE.ENDOFCHAIN Or ID = SECTORTYPE.FREESECT Then

            Exit Sub

        End If

        '添加DIFAT

        AddItem2List DIFAT, ID

   Next i

   '读取下一个DIFAT扇区的ID

   Get FS, , ID

   '采用递归,调用自己继续读取

   ReadDIFATSector FS, DIFAT, ID, SectorSize

End Sub

首先用ReadDIFAT方法,来读取Header中的前109个DIFAT信息,然后判断是否存在DIFAT扇区,若存在,则调用方法读取该扇区。在ReadDIFATSector中,顺序读取前127个DIFAT信息,然后读取最后一个信息,即为下一个DIFAT扇区的位置,然后调用方法本身继续读取。

注:代码中使用了一个AddItem2List的方法,把数据添加到数组中。

5.6 使用VBA解析FAT

解析FAT的程序与解析类似,唯一不同的是,FAT扇区的读取顺序是由DIFAT数组决定的。所以在读取FAT的时候,需要以此遍历DIFAT数组,到指定的FAT扇区中读取FAT数据。

参考代码如下:

'读取FAT

Sub ReadFat(FS As Integer, DIFAT() As Long, FAT() As Long, SectorSize As Long)

   Dim i   As Long

   '根据DIFAT的顺序。读取每个FAT扇区

   For i = 0 To UBound(DIFAT)

        ReadFATSector FS, FAT(), DIFAT(i),SectorSize

   Next i

End Sub

 

 

'读取指定扇区的FAT

Sub ReadFATSector(FS As Integer,FAT() As Long, SectorID As Long, SectorSize As Long)

   Dim i   As Long

   Dim ID  As Long

   '定位

   Seek FS, GetFileOffset(SectorID, SectorSize)

   '读取FAT。在一个FAT扇区中,有128个FAT数据

   For i = 0 To 127

        '读取数据

        Get FS, , ID

        '添加至FAT

        AddItem2List FAT, ID

   Next i

End Sub

 

5.7 使用VBA解析MINIFAT

至于解析MINIFAT,留给大家自己完成吧。其方法和FAT类似,其起始扇区ID存放在Header信息中,至于顺序嘛,当然是存在FAT中哦,不能搞错哦。

5.8 小结

本章的内容至关重要,直接关系到能否读取正确的数据。后面,我将来介绍如何用FAT信息和MINIFAT信息正确读取数据。

 5.9 示例代码下载

如果你懒得写代码,那么可以直接下载附件。点击【阅读原文】即可。我在文件中准备了两个不同的文件供测试,一个大的文件是有DIFAT扇区的,一个则没有。大家可以思考一下,什么样的文件才会有DIFAT扇区?具备DIFAT扇区的文件有什么特征呢?

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章