李懿 Excel學習 2016-07-05
微信掃一掃
關注該公衆號
需要之前的文章,請在公衆號內回覆:
複合文檔
5、讀取DIFAT,FAT,MINIFAT
上一次,我們成功讀取了文件頭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扇區的文件有什麼特徵呢?