討論區快速選單
知識庫快速選單
傑米的攝影旅遊筆記 網路投保旅行平安險
[ 回上頁 ] [ 討論區發言規則 ]
[心得]Office檔案格式
更改我的閱讀文章字型大小
作者 : chiuinan2(青衫)討論區板主 Visual C++ .NET卓越專家VC++一代宗師Visual Basic優秀好手資訊類作業求救卓越專家一般曠世奇才程式設計甘苦談優秀好手C++ Builder優秀好手上班族的哈拉園地優秀好手C++頂尖高手Assembly優秀好手貼文超過3000則人氣指數超過150000點
[ 貼文 3732 | 人氣 170106 | 評價 34520 | 評價/貼文 9.25 | 送出評價 125 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2004/9/29 下午 03:29:28
** 這是我去年撰寫office檔剖析程式時, 順手寫下來的文件. 先貼一部份看看大家需不需要了解...

1. Ole物件檔

Office檔案或是Embeded Object,這些檔案都是透過IStorage界面來儲存的,一般稱為OLE物件檔(也稱為Laola檔)。什麼是IStorage界面呢?它是Windows所提供的一個OLE界面,主要是提供給OLE物件做為儲存資料之用。IStorage之所以好用,主要是它提供類似一個目錄/子目錄/檔案的階層式組織,統包在一個檔案裡,如此其他物件便可以在同一個檔案裡,以目錄階層的方式,儲存多種不同的資料。因此要解Office檔,首先必須要弄清IStorage所儲存的OLE物件檔格式。

為了快速存取類似目錄檔案的結構,IStorage模仿了類似實際的目錄檔案結構。它將檔案中每512 byte視為一個單位,稱為大區塊資料(BBD,Big Block Data)。不過說實在的,這些名稱真的很容易令人混淆不清(看過MS的文件就會知道,因為還有很多定義的用字都很接近)。因此這邊我不沿用MS的名稱定義,大家把它想成是一個磁區(sector)就對了,反正IStorage就是在模仿磁碟目錄結構,直接使用磁碟的用詞反而容易懂。而為了管理這些磁區,當然就要有FAT(檔案磁區配置表,MS稱它為大區塊庫,真難懂)。不過檔案一詞在這邊反而會混淆,因此儲存在IStorage的“檔案”,我便沿用MS的名稱,稱為資料串(stream)。以下便開始從檔頭說起:

檔頭當然就是在檔案的開頭處,剛好是一個磁區(512 byte)。由於這個檔頭是固定有的,不能被使用,因此實際的磁區位置,必須從512 byte開始計算起。也就是說Sec#0的位置在512,Sec#1的位置在1024, 以此類推。以下便是檔頭的重要資料:

00h (8 byte):檔頭標記,一開始的前8個byte固定為D0 CF 11 E0 A1 B1 1A E1,否則便不是OLE物件檔
2Ch (long):FAT使用的磁區數
30h (long):檔案目錄結構屬性開始的磁區
3Ch (long):小資料儲存區FAT開始的磁區
44h:額外記錄FAT使用磁區的開始磁區
48h:額外記錄FAT使用磁區的磁區數
4Ch開始:FAT使用的磁區(long),數量由2Ch中的磁區數決定

這邊注意到有一個小資料儲存區。由於每個磁區都是512 byte,拿來放小資料的話,會非常浪費空間,因此IStorage便將較小的資料,統一另行儲存。各位可以將小資料儲存區想成是另一個檔案,這個檔案又是模擬目錄檔案結構,只是每個磁區縮小到64 byte而已。這個部份我待會再談,先將基本512 byte磁區的模擬方式弄懂,小資料儲存區的格式便更容易懂了。

由於在IStorage中,所有資料在磁區的儲存次序,都和FAT有關,因此必須先弄清FAT的配置方式。從檔頭中,我們可以知道FAT使用了那些磁區,將這些磁區的資料組合在一起,便是真正的FAT資料(磁區可能跳來跳去的)。而在FAT資料裡,其實便是記錄著每個磁區的下一個磁區是什麼(每筆資料均為long)。 這邊的磁區值可能為:

0xfffffffd (-3):特殊區塊(FAT使用的磁區便是)
0xfffffffe (-2):結束標記(表示已無下個資料磁區)
0xffffffff (-1):尚未被使用的磁區
其他:下個資料所在的磁區

因此如果知道一個資料串從那個磁區開始,便可以直接參照FAT,看看資料所在的下個磁區是在那裡。例如Sec#10,便查看FAT中第10個(由0編起)的long值磁區編號,便是下一個。如此一路查下去,便可以得到整個資料串所使用的磁區數和次序。應該很簡單吧?

這裡有一個情況必須特別處理的,那就是超大型的OLE檔。由於FAT表使用的磁區是放在Ole檔頭4Ch開始的地方,但因為檔頭只有512 byte而已,因此只能記錄109個磁區(約為7MB左右)。如果Ole檔更大,使得FAT使用的磁區表記錄空間不夠使用時,便必須讀取44h所指的磁區,視為下一個記錄FAT使用磁區的磁區。只是如果還是不夠儲存時,再下一個磁區在那裡呢?其實它是記錄在該磁區內容的最後一個值,以串列形式組成(-1表示結束)。因此在讀取整個FAT表時,必須考慮到此一情形。
作者 : chiuinan2(青衫)討論區板主 Visual C++ .NET卓越專家VC++一代宗師Visual Basic優秀好手資訊類作業求救卓越專家一般曠世奇才程式設計甘苦談優秀好手C++ Builder優秀好手上班族的哈拉園地優秀好手C++頂尖高手Assembly優秀好手貼文超過3000則人氣指數超過150000點
[ 貼文 3732 | 人氣 170106 | 評價 34520 | 評價/貼文 9.25 | 送出評價 125 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2004/9/29 下午 03:30:38
接下來我們來看看檔案目錄結構屬性的資料(起始磁區記錄在檔頭)。這個部份也是一個資料串,因此算出來的位置,都是相對於資料串,你必須換算成是第幾個磁區,然後再從FAT裡得到實際所在的磁區。例如所要的資料是在第540 byte處,那麼由資料串開頭算起是Sec#1,如果在FAT表裡查到這個資料串的磁區編號為{9,13,22,41},實際所在的磁區便是Sec#13(offset也要重算)。不懂的話請再想想弄清楚。

檔案目錄結構屬性的資料,每個佔128 byte,並以指標加以串連。以下便是每個結構屬性的值(第一個結構屬性就是根目錄):

00h:共64 byte,記錄資料串名稱(unicode),根目錄一律為"Root Entry"(把它想成是檔名或目錄名就對了)
40h (short):資料串名稱的byte長度,0表這個屬性沒有用到(deleted)
42h (char):本結構屬性的形態,1=目錄,2=檔案,5=根目錄
44h (long):上一個結構屬性指標,-1表沒有
48h (long):下一個結構屬性指標,-1表沒有
4Ch (long):若本結構屬性的形態為目錄或根目錄,則指向本目錄裡各子目錄/檔案的第一個結構屬性(指標)
74h (long):資料所在的起始磁區
78h (long):資料的byte數

大家可以看到,這個結構其實和磁碟的檔案目錄結構沒什麼兩樣,同樣也是樹狀階層式的。其中結構屬性指標指的是第幾個結構屬性值(由0編起),相對於資料串起點的位置,便是指標值*128。

有一點必須特別注意的是,上一個/下一個結構屬性指標並非雙向鏈結,而是隨意鍵結,例如:

attr#3:上一個是#2,下一個是#4
attr#2:上一個是#5,沒有下一個
attr#5:沒有上一個,沒有下一個
attr#4:沒有上一個,沒有下一個

因此共計有2,3,4,5等4個結構屬性。也就是說,你必須將所有鍵結的結構屬性都展開到,才能得知整層的檔案目錄資料。

另外,資料所在的起始磁區,可能指向標準的512 byte磁區,也可能指向小資料儲存區裡的64 byte磁區,其分別在於資料的byte數。若byte數>=4096,便是儲存在512 byte磁區,否則便是儲存在64 byte磁區。 唯一例外的是根目錄,一律儲存在512 byte磁區中。

至於資料串的內容,除了根目錄Root Entry外,其他都是使用者自己訂的。因此每個資料串裡的資料如何安排,表示什麼意思,都必須另行處理,這點無關IStorage的事。IStorage只是盡責地,將使用者要儲存的資料,依照上面的格式儲存下來而已。

接下來開始說明小資料儲存區的部份。在檔頭3Ch的地方記錄了小資料儲存區FAT開始的磁區。小資料儲存區FAT也是一個資料串,必須將整個資料串讀入後才能處理。這個小資料儲存區FAT裡的資料格式,和之前提到FAT資料格式都是完全一模一樣,以串列的方式來記錄各資料串使用的磁區。但小資料磁區裡的資料實際上在那裡呢?其實就是根目錄裡的資料串。這也就是為什麼根目錄的資料,一律都是放在標準512 byte磁區裡。而在根目錄資料串裡,便是以64 byte為一單位,切割成小磁區,供小資料存放使用。於是, 要取得一個資料串的過程,便成為:

(1) 在檔案目錄結構裡,找出該資料串相同名稱的屬性。屬性裡的資料磁區指標和大小,便是資料所在的位置。
(2) 如果資料串的資料,是在標準512 byte磁區中,便到FAT表裡找出該資料串使用的磁區,一個一個依次載入。注意OLE檔可能不是剛好512 byte,如果是最後一個sector,必須只讀取檔案長度剩餘的部份。
(3) 如果資料串的資料,是在小資料儲存區中,便必須載入根目錄的整個資料串,然後到小資料儲存區FAT表裡,找出該資料串使用的小資料磁區,再一個一個從根目錄資料串的小磁區裡載入。

其實整個OLE物件檔的結構應該算是很簡單。各位有空可以去看一下類似的文件( http://user.cs.tu-berlin.de/~schwartz/pmh/guide.html),即使你都已經弄懂了OLE物件檔,還是可能看不懂這些文件。
作者 : chiuinan2(青衫)討論區板主 Visual C++ .NET卓越專家VC++一代宗師Visual Basic優秀好手資訊類作業求救卓越專家一般曠世奇才程式設計甘苦談優秀好手C++ Builder優秀好手上班族的哈拉園地優秀好手C++頂尖高手Assembly優秀好手貼文超過3000則人氣指數超過150000點
[ 貼文 3732 | 人氣 170106 | 評價 34520 | 評價/貼文 9.25 | 送出評價 125 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2004/9/29 下午 07:44:02
2. Office檔的摘要內容

Office檔的摘要主要有兩個部份,分別放在"\005DocumentSummaryInformation"和"\005SummaryInformation"這兩個資料串中。以下便分別加以說明(請配合MS Word的摘要設定操作來看比較容易懂):

(1) "\005DocumentSummaryInformation"資料串

18h (long):GUID數目
1Ch開始每20 byte:依次存放{GUID+屬性組資料位置},前者為16 byte,後者為long

在這個資料串裡,可以記錄兩種屬性組,一個是DocumentSummaryInformation,一個是UserDefinedProperties。以下便是這兩個屬性組的GUID:

DocumentSummaryInformation:
0x02,0xD5,0xCD,0xD5,0x9C,0x2E,0x1B,0x10,0x93,0x97,0x08,0x00,0x2B,0x2C,0xF9,0xAE
UserDefinedProperties:
0x05,0xD5,0xCD,0xD5,0x9C,0x2E,0x1B,0x10,0x93,0x97,0x08,0x00,0x2B,0x2C,0xF9,0xAE

使用者自訂的摘要部份待會再說,底下便先針對標準的摘要部份,也就是DocumentSummaryInformation。從上述的GUID比對到後,後面便是指向這個屬性組(Property Set)的資料區塊。屬性組資料區塊的格式如下(這邊的位置都是相對於資料區塊,而非整個資料串):

00h (long):資料區塊大小
04h (long):屬性數目
08h開始每8 byte:屬性編號(long)+屬性資料位置

標準的摘要屬性編號(Property ID)是固定的,以下便是各編號的意義(大部份配合MS Word摘要設定就能懂了):

01: CodePage, long - 屬性組文字資料使用的編碼方式(固定的ProperSet ID),這個部份我最後再來說明
02: Category, 字串 - 類別
03: PresentationTarget, 字串 - 展示方式(印表機/螢幕),PowerPoint在用的
04: Bytes, long - 文件byte數
05: Lines, long - 文件行數
06: Paragraphs, long - 文件段落數
07: Slides, long - 文件Slides數,PowerPoint在用的
08: Notes, long - 有註記的頁數,PowerPoint在用的
09: HiddenSlides, long - 隱藏的Slides數,PowerPoint在用的
0A: MMClips, long - 聲音/影片數,PowerPoint在用的
0B: ScaleCrop, bool - 是否需要縮放Thumbnail,FindFile在用的
0C: HeadingPairs, variant/vector - Office內部在用的,不管它
0D: TitlesofParts, 字串/vector - 所有的文件名稱(如Excel的Sheet名稱,PowerPoint的Slide標題等等)
0E: Manager, 字串 - 主管
0F: Company, 字串 - 公司
10: LinksUpToDate, bool - Office內部在用的,不管它

至於屬性的資料,其格式為:

屬性資料形態(long)+實際屬性資料

由於我們的目的是要建索引,因此只需取出文字的屬性資料即可。文字屬性的資料形態為0x1E,實際的屬性資料則為:

字串資料byte數+字串資料

如果要解使用者自訂的屬性組,只需找出所有的屬性資料,只要是文字屬性的,都加以讀出即可。
作者 : chiuinan2(青衫)討論區板主 Visual C++ .NET卓越專家VC++一代宗師Visual Basic優秀好手資訊類作業求救卓越專家一般曠世奇才程式設計甘苦談優秀好手C++ Builder優秀好手上班族的哈拉園地優秀好手C++頂尖高手Assembly優秀好手貼文超過3000則人氣指數超過150000點
[ 貼文 3732 | 人氣 170106 | 評價 34520 | 評價/貼文 9.25 | 送出評價 125 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2004/9/29 下午 07:45:23
(2) "\005SummaryInformation"資料串

其格式和"\005DocumentSummaryInformation"完全一模一樣,差別只有屬性編號的意義。以下我只挑出重要的列一下:

GUID:0xE0,0x85,0x9F,0xF2,0xF9,0x4F,0x68,0x10,0xAB,0x91,0x08,0x00,0x2B,0x27,0xB3,0xD9

02: Title, 字串 - 標題
03: Subject, 字串 - 主旨
04: Author, 字串 - 作者
05: Keyword, 字串 - 關鍵字
06: Commenct, 字串 - 註解

其中若標題沒有在本資料串時,應該到前述DocumentSummaryInformation資訊裡的TitlesofParts中取得。

關於編碼方式(Code Page)的重要值域:

932: 日文
936: 簡體中文
949: 韓文
950: 繁體中文
1200: Unicode
1252: 英文

如果取得的屬性值料是字串的話,便必須依照指定的編碼方式進行轉碼的動作。
作者 : chiuinan2(青衫)討論區板主 Visual C++ .NET卓越專家VC++一代宗師Visual Basic優秀好手資訊類作業求救卓越專家一般曠世奇才程式設計甘苦談優秀好手C++ Builder優秀好手上班族的哈拉園地優秀好手C++頂尖高手Assembly優秀好手貼文超過3000則人氣指數超過150000點
[ 貼文 3732 | 人氣 170106 | 評價 34520 | 評價/貼文 9.25 | 送出評價 125 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2004/9/29 下午 07:47:14
3. Word 97的格式

Word檔案的資料,主要是記錄在"WordDocument"資料串與"0Table"/"1Table"資料串中,由於文字資料主要是記錄在"WordDocument"資料串中,因此我們先從此一部份著手。至於Embeded Object是另行記錄的, 這我們最後再來說明。

"WordDocument"資料串一開頭的地方,稱為FIB(File Information Block),裡面記錄了各種重要的資訊與指標。因此要解出文字資料,首先必須弄懂FIB。以下便列出FIB比較重要的部份:

0002h (short):版本
000Ah (short):狀態旗標(以bit0為最低位元)
bit 2 (mask=0x0004):是否為複雜格式
bit 8 (mask=0x0100):檔案是否加密
bit 9 (mask=x00200):0表使用"0Table"資料串, 1表"1Table"資料串
bit 14 (mask=0x4000):是否為遠東版
000Eh (short):加密鍵值
0018h (long):文字資料起始位置(非複雜格式時)
001Ch (long):文字資料結束位置+1(非複雜格式時)
0020h (short):後面的短整數參數數目
0022h開始:短整數參數,比較重要的是第13個(由0編起,003Ch),若為遠東版,則這個參數記錄了語系ID(後述)
????h (short):後面的長整數參數數目
????h開始:長整數參數
#0:資料串長度
#1:建立日期
#2:修改日期
#3:文件(document)文字長度
#4:註腳(footnote)文字長度
#5:頁眉(header)文字長度
#6:巨集(macro)文字長度
#7:annotation文字長度
#8:endnote文字長度
#9:文字塊文字長度
#10:頁眉文字塊文字長度
????h (short):FC/LCB數目
????h (long/long):FC/LCB資料
#33:piece table位置/大小

其中處理起來比較麻煩的是複雜格式,這是當使用者使用快速存檔(Quick Save)時,才會形成的格式,這部份後面再說明。至於加密的文件,目前不予以處理(要自行對Word檔解密,會花太多時間,同時加密文件無法建索引,應該是很正常的)。

關於語系ID的值域,bit 0-9 = 主語系,bit 10-15 = 次語系,相關資料可以參考MSDN,以下是一些辨識方法:

語系ID = 0x0404表繁體中文,0x0804表簡體中文
主語系 = 0x09表英文,0x11表日文,0x12表韓文

這些語系資料可以提供,當Word檔裡面記錄的不是Unicode時,應該如何轉碼。這樣即使外界設錯內碼格式,我們仍能正確轉成Unicode處理。以下便開始說明非複雜非加密格式的Word文件如何抽出文字資料。

作者 : chiuinan2(青衫)討論區板主 Visual C++ .NET卓越專家VC++一代宗師Visual Basic優秀好手資訊類作業求救卓越專家一般曠世奇才程式設計甘苦談優秀好手C++ Builder優秀好手上班族的哈拉園地優秀好手C++頂尖高手Assembly優秀好手貼文超過3000則人氣指數超過150000點
[ 貼文 3732 | 人氣 170106 | 評價 34520 | 評價/貼文 9.25 | 送出評價 125 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2004/9/29 下午 07:48:36
Word的文字資料,分成document,footnote,header...等好幾個部份(參見FIB裡的長整數參數),這些文字資料都是相連接在一起儲存的。儲存的起終位置便記錄在FIB的18h,1Ch裡。然而我們卻無法直接到裡面取出文字資料,因為這些文字資料是以512 byte為一單位放在一起,而且語系並不一定相同(可能是ASCII,也可能是Unicode,這樣做的目的當然是要檔案小一些)。因此我們必須藉助piece table的資訊來取得真正的文字資料。至於如何取得,待會再來說明。因為較早期的word檔並沒有piece table,這種情況下表示文字資料是以同一種語系儲存的,如此取得文字資料的方式就很簡單,只需到文字資料起始位置開始,依照FIB長整數參數裡記錄的各部份文字長度,一個一個加以讀出處理便可以了。不過在處理前必須先辨別儲存的文字是ASCII形式,還是Unicode形式。方法就是:

文字byte數=文字資料結束位置-文字資料開始位置
文字總字數=文件文字長度+註腳文字長度+頁眉文字長度+....

如果文字總字數*2=文字byte數(unicode每個字是2 byte),便是unicode形式。不過由於Word有時在文字資料最後面會多加一個段落標記(總字數少了),因此判斷時要以"文字總字數*2<=文字byte數"為準。另外,取出的Word文字資料裡也有一些控制字元必須特別加以處理,這些字元包括(以ASCII字碼10進位列出):

07:cell mark
09:tab
11:break line
12:page break/section mark
13:paragraph end
14:clumn break
19:field begin
20:field seperator
21:field end
30:non-breaking hyphen
31:non-required hyphen
160:non-breaking space

如果字串是ASCII形式,則還會有以下的一些特殊字元:

85h:...
92h:'
93h:"
96h:--
97h:---

其中比較需要注意的是field start(19)/field seperator(20)/field end(21)等三個字元。這些字元是用來夾住word的一些特殊標記文字,例如hyperlink的相關資訊,以及"目錄"等由word自行做出的結構,其中field start到field seperator之間的字串是控制用的(不顯示),field seperator到field end之間的字串則是顯示用的,故前半部的資料應瀘除,後半部的資料要取出。如果沒有特別處理的話,便會出現一堆如"HYPERLINK \l "TOC1899651""等無意義字串。

如果有piece table時,文字資料便不能像前面所說的,直接判斷並加以讀取,必須經由piece table的資訊加以判斷。piece table資訊是記錄在Table資料串,至於是使用"0Table"資料串,還是"1Table"資料串,可由FIB裡的資訊得知。取得Table資料串後,piece table的資訊為:

(byte) 1
(short) grpprl大小
grpprl
(byte) 1
(short) grpprl大小
grpprl
...
(byte) 2
(long) plcfpcd大小
plcfpcd

我們要的是plcfpcd的資訊,因此必須將grpprl全部略過。plcfpcd主要由PLC和PCD兩個陣列所組成,因此必須先算出元素數目:(plcfpcd大小-4)/12。其中PLC元素數目要再加一。PLC陣列的主要目的,是用來記錄累積的文字數,因此第i個文字piece的文字字數,便是PLC[i+1]-PLC[i],這也就是為何PLC元素要多一個的原因。PCD陣列,主要用來取得文字piece的位置,其元素格式如下:

(short) 狀態值
(long) 文字piece的位置,最高的第二個位元(0x40000000)若為1,表示文字是ASCII,否則為Unicode
(short) 記錄PRM或grpprl的索引值(這部份無關重要,可以不管)

因此要解出所有文字資訊,只須依次取得各文字piece的位置/字數,並決定為何種語系,再加以讀出處理即可。不過要注意的是,文字piece的位置,當形式為ASCII時,其位置會x2,因此換算成實際位置時,必須再除以2。
作者 : chiuinan2(青衫)討論區板主 Visual C++ .NET卓越專家VC++一代宗師Visual Basic優秀好手資訊類作業求救卓越專家一般曠世奇才程式設計甘苦談優秀好手C++ Builder優秀好手上班族的哈拉園地優秀好手C++頂尖高手Assembly優秀好手貼文超過3000則人氣指數超過150000點
[ 貼文 3732 | 人氣 170106 | 評價 34520 | 評價/貼文 9.25 | 送出評價 125 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2004/9/29 下午 07:49:35
至於複雜格式是什麼呢?其實就是文字資料並沒有集中放在一起,而是隨著編輯過程而分散在各處。要取得這些文字資料,其實只須依照piece table裡面的資訊來取便可以了。

4. Word 95的格式

Word 95的檔案格式,基本上和Word97差不多,然而由於該時期的版本並未支援Unicode,因此檔案中文字的編碼並非Unicode,而是以一種很怪、類似於Unicode的方式儲存。也就是說,中/英文都是2 byte,但中文記錄的不是Unicode,而是它的兩個ASCII字碼。例如"一"的BIG5碼是A440h,它便將A440h視為一個2 byte字碼儲存起來,因此先存40h,再存A4h。於是文字資料讀取以後,還是必須進行轉碼的動作,才能得到實際的Unicode。

5. Word更早期版本的格式

Word更早期版本的檔案格式,本身並非OLE物件檔。事實上,該檔案的內容便是OLE裡的WordDocument資料串。也就是說,當IStorage界面製訂出來以後,word便將整個檔案視為資料串,存到OLE物件檔裡。因此要解這種早期版本,只需直接將它視為WordDocument取出的資料串,然後一樣到18h的地方讀取文字位置和長度,即可解出文字資料。不過這種早期版本的word檔,還沒有Unicode的觀念,因此存的全部都是ASCII碼。
作者 : chiuinan2(青衫)討論區板主 Visual C++ .NET卓越專家VC++一代宗師Visual Basic優秀好手資訊類作業求救卓越專家一般曠世奇才程式設計甘苦談優秀好手C++ Builder優秀好手上班族的哈拉園地優秀好手C++頂尖高手Assembly優秀好手貼文超過3000則人氣指數超過150000點
[ 貼文 3732 | 人氣 170106 | 評價 34520 | 評價/貼文 9.25 | 送出評價 125 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2004/9/29 下午 09:22:08
6. RTF檔格式

.doc的檔案,不只是Word檔格式而已,還可能是RTF檔或是純文字檔,因此在處理前必須先行判斷。以下便針對RTF檔的格式進行說明。在說明RTF的格式之前,我們先看一下RTF的一個簡單範例:

{\rtf1\ansi\ansicpg950 {\fonttbl ...} ...}

RTF檔的內容,主要由三個部份所組成,一是命令,也就是以\開頭的字;一是群組,也就是{}括起來的部份;最後一個當然就是資料。RTF命令的格式如下:

\<keyword><number><delimitor>

keyword必須都是英文字母(RTF檔是大小寫有關的),或是單一特殊字元。number可有可無,當有的時候,便做為命令的參數。這個數字可能是負的(以‘-’做開頭)‘而且RTF裡的數字一律為2 byte的短整數。delimitor可以是空白,或者任何一個非英文字母的字元,若是空白便必須將它吃掉,不視為資料處理。

RTF的命令,主要可分為下列三種:

(1) 資料屬性定義命令:用來定義資料的相關屬性,例如語系、字型、版面等等
(2) 資料意義命令:用來說明資料實際的意義,例如內文、註腳、頁眉、字型表等等
(3) 特殊字元命令:用來輸入一些特殊字元

除了上述三種命令之外,還有一個特別的命令\*,這個命令是表示如果後面緊接著的命令不懂的話,可以將其後的資料全數略過。這個命令主要是提供給應用程式,以便植入一些自己定義的命令與資料。因此遇到\*命令時,必須再讀取下個命令,才能決定是要處理,還是要全數略過捨棄。如果命令是在一個群組之中(即在{}之內),則該命令只作用在該群組裡其後的資料(包括下層群組),當離開群組後,資料的屬性必須回復到外層群組的屬性。以下便開始說明這三種RTF的命令(只列出與取文字有關的):

(1) 資料屬性定義命令

\rtf:RTF的檔頭標記,後面的數字為RTF的版本(目前都是1)
\ansi:使用ANSI字集
\mac:使用Apple Macintosh字集(目前不支援,視為錯誤)
\pc:使用IBM PC code page 437字集(目前不支援,視為錯誤)
\pca:使用IBM PC code page 850字集(目前不支援,視為錯誤)
\ansicpg:使用的語系(實際語系需視字型語系而定),後面的數字可為:
932 日文
936 簡體中文
949 韓文
950 繁體中文
\langfenp:使用的字型語系,1028 = 繁體中文,2052 = 簡體中文
\ud:資料採用Unicode編碼(\u命令)
\upr:後面接兩個群組,第一個群組是ANSI編碼,第二個群組是Unicode編碼,可任挑一個做為資料處理(這是為了與無法處理Unicode的RTF Reader相容所設,若能處理Unicode,應以Unicode為準)

(2) 資料意義命令

\info:摘要資訊
\title:標題
\author:作者
\subject:主旨
\manager:主管
\company:公司
\doccomm:文件註解
\comment:註解(無作用,可省略的註解)
\category:類別
\keywords:關鍵字
\userprops:使用者自訂屬性(Word自己的定義)
\propname:使用者自訂屬性的屬性名稱(Word自己的定義)
\staticval:使用者自訂屬性的屬性值(Word自己的定義)
\header:頁眉
\footer:頁腳
\footnote:註腳
\fonttbl:字型表
\colortbl:顏色表
\stylesheet:樣式表
\pict:圖片
\shptxt:方塊文字資料(Word自己的定義)
\shpinst:方塊文字開始(Word自己的定義)

未遇到上述命令前的資料,一律為內文。
作者 : chiuinan2(青衫)討論區板主 Visual C++ .NET卓越專家VC++一代宗師Visual Basic優秀好手資訊類作業求救卓越專家一般曠世奇才程式設計甘苦談優秀好手C++ Builder優秀好手上班族的哈拉園地優秀好手C++頂尖高手Assembly優秀好手貼文超過3000則人氣指數超過150000點
[ 貼文 3732 | 人氣 170106 | 評價 34520 | 評價/貼文 9.25 | 送出評價 125 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2004/9/29 下午 09:23:28
(3) 特殊字元命令

\{:'{'字元
\}:'}'字元
\-:'-'字元
\_:'-'字元
\~:空白字元
\':16進位字元,後面接兩個16進位數字,例如\'A4表示A4h字元
\u:Unicode字元,後面的數字即為Unicode字碼,如果命令結束後緊接著一個'?',要將之吃掉
\emspace:空白字元
\enspace:空白字元
\qmspace:空白字元
\emdash:'-'字元
\endash:'-'字元
\lquote:單引號
\rquote:單引號
\ldblquote:雙引號
\rdblquote:雙引號
\tab:TAB字元
\trowd:表格開始
\row:表格行結束
\nestrow:表格行結束
\cell:表格欄結束
\nestcell:表格欄結束
\column:column break
\page:page break
\line:line break
\par:段落結束
\sect:段落/區段結束

因此要抽取RTF文字資料,只需一層一層群組地剖析下去,當遇到資料時,看看資料是什麼意義,是什麼樣屬性,即可抽取出來。但注意在處理資料時,若是遇到換行字元(ASCII 13,ASCII 10),必須將之略去不處理。

7. Word檔剖析時的一些注意事項

(1) 有些RTF或文字檔,檔尾有補一個ASCII 0,因此在辨識是否文字檔時,最後一個byte不應檢查
(2) 有些.doc檔,其實是gif/zip/pdf/html等檔案rename成的,像gif/zip,因有ASCII 0可以簡單辨識,但pdf/html為純文字檔,必須特別處理。html可加以辨識後,轉交html剖析物件處理,pdf則會形成許多看似中文的亂碼,因此必須特別檢查是否合法中文值域,以便略過這些檔案。
(3) 有些.doc檔,裡面會有西歐字元,會造成與中文值域相衝。因其他資料都是好的,只須將西歐字元瀘除即可。但這部份與前一注意事項會衝突,目前的做法是,如果有連續兩個中文值域相衝的碼,整個檔案才不處理,否則便僅瀘除該不合法中文字。
(4) 有些損毀的word檔,前頭為純文字,後面為亂碼,因此除了檔頭外,也必須檢查整個檔案是否有出現ASCII 0,才能得知是否為合法純文字資料。但我碰過一個檔案,裡面恰好有一個ASCII 0字元,其餘部份都是正常的。由於word會瀘除ASCII 0字元(顯示時是"口"),因此這部份的檢驗必須加以調整。目前的條件是,連續出現三個ASCII 0,或是累積達到10個時,整個檔案才不處理。

由於word並不管檔案中是否有亂碼,均全數載入,但編碼方式由使用者決定,因此有時載入後完全都是亂碼,或是局部有一些亂碼,其餘都是正常的。然而在建索引時,這些亂碼資料其實是亳無意義的,因此才會加入這些辨識的部份,避免亂碼資料進入索引資料中。但為避免全部資料只有一些些亂碼,導致整個資料被瀘除的狀況,才會有這種似要瀘除,卻又有條件允許的奇怪條件(確實挺麻煩的)。不過基本上,只要是合法的word檔,幾乎都能正確解出,上面的許多情況大都是一些人為做出的特例。

作者 : chiuinan2(青衫)討論區板主 Visual C++ .NET卓越專家VC++一代宗師Visual Basic優秀好手資訊類作業求救卓越專家一般曠世奇才程式設計甘苦談優秀好手C++ Builder優秀好手上班族的哈拉園地優秀好手C++頂尖高手Assembly優秀好手貼文超過3000則人氣指數超過150000點
[ 貼文 3732 | 人氣 170106 | 評價 34520 | 評價/貼文 9.25 | 送出評價 125 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2004/9/29 下午 09:26:06
8. PowerPoint 97的格式

PowerPoint檔主要有三個資料串,"Current User"記錄了最近一次開檔修改的使用者相關資訊,"Pictures"記錄了所有的圖片視訊資料,這兩個資料串均不必處理。主要的內容則記錄在"PowerPoint Document"這個資料串裡,因此底下便只剖析這個資料串。
 
"PowerPoint Document"資料串裡的資料,係以record為單位組成的,而這些record可區分為container和atom兩種。這兩種record的作用,有點像是目錄/檔案結構,atom是實際記錄資料/屬性的地方, container則是用來將record做成樹狀結構。以下便是每個record的格式:

2 byte:最低4 bit為版本,最高12 bit為record次形態值
2 byte:record的形態值
4 byte:record內容的長度
? byte:reocrd內容

因此我們所要做的,便是從資料串開始處,一個一個record讀入,辨識找出所需的record(包括下層的record),然後抽取文字出來。至於需要那些record,主要由record的形態值來加以辨識。以下便是需要的record(後面的數字便是它的record形態值):

Document (1000,container):內含PowerPoint主要的文件內容,以EndDocument做為結束
EndDocument (1002,atom):Document內容結束標記
SlideListWithText (4080,container):內含各Slide的文字資料(只有title和body文字資料)
SlidePersistAtom (1011,atom):記錄Slide相關資訊(可用以區分不同Slide)
MainMaster (1016,container):記錄母片相關資訊
Notes (1008,container):記錄備忘稿相關資訊
Slide (1006,container):內含一個Slide的資料(title/body以外的文字資料放在此處)
SlideAtom (1007,atom):用以得知該Slide是否有備忘稿
HeadersFooters (4057,container):內含頁首頁尾相關資訊
PPDrawing (1036,container):Office藝術家資料(文字方塊置於此處)
TextHeaderAtom (3999,atom):用以表示其後的文字atom的意義(可用以區分文字是否分隔成不同區塊)
TextBytesAtom (4008,atom):Unicode均<256的文字內容
TextCharsAtom (4000,atom):Unicode文字內容
CString (4026,atom):Unicode文字內容
ExOleObjStg (4113,atom):儲存embeded object實際內容(以後再說明)
Unknown (0,atom):不明形態(可用以辨別是否結束)
 
我們先看Document的結構形態(以下的內容都已經將不必要的record瀘除):

Document
   SlideListWithText
     SlidePersistAtom -- 第1個slide
     TextHeaderAtom
     <text>
     TextHeaderAtom
     <text>
     SlidePersistAtom -- 第2個slide
     SlidePersistAtom -- 第3個slide
     TextHeaderAtom
     TextHeaderAtom
     <text>
     SlidePersistAtom -- 第4個slide
     TextHeaderAtom
     TextHeaderAtom
   EndDocument

作者 : chiuinan2(青衫)討論區板主 Visual C++ .NET卓越專家VC++一代宗師Visual Basic優秀好手資訊類作業求救卓越專家一般曠世奇才程式設計甘苦談優秀好手C++ Builder優秀好手上班族的哈拉園地優秀好手C++頂尖高手Assembly優秀好手貼文超過3000則人氣指數超過150000點
[ 貼文 3732 | 人氣 170106 | 評價 34520 | 評價/貼文 9.25 | 送出評價 125 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2004/9/29 下午 09:26:47
由於Document必是第一個record,因此如果要抓取title/body的文字,只要找到SlideListWithText,然後依次往下抓,便可以取到所有的title/body文字(還要考慮SlideListWithText的次型態值,這點後面再說明)。然而每個Slide並不一定只有title/body,裡面可能有許多的表格或文字方塊,這些資料並不放在Document中。此外,PowerPoint的資料還包括母片與備忘稿等,因此我們再看一下整個檔案的結構形態:

Document
   <...>
   EndDocument
MainMaster
   PPDrawing
Slide -------------- 第1個slide
   HeadersFooters
     <text>
   SlideAtom
   PPDrawing
Slide -------------- 第2個slide
   HeadersFooters
     <text>
   SlideAtom
   PPDrawing
...
Notes
   PPDrawing
Notes
   PPDrawing
...
Unknown

母片的資料便放在MainMaster中,每一個Slide的頁首頁尾與其他文字,則放在Slide裡,至於備忘稿,便放在Notes裡。Slide的數目,通常會和SlideListWithText相同,但也會有一些如標題Slide會放於此處,分辨方法便是檢查SlideAtom offset 0的長整數(4 byte), 如果是2,表示是title master slide,屬於母片的slide,否則便是正常的Slide。至於Notes的數目並不一定,應配屬於那個Slide,我們後面再說明。

這邊需要注意的是,所有非title/body的文字,都是儲存在PPDrawing這個container裡,但其格式依文件是Office藝術家的格式,其結構並未說明。但據我測試結果,和PowerPoint的record結構沒什麼兩樣,其文字資料是放在:

PPDrawing
  -4094 (containter)
   -4093 (containter)
    -4092 (containter)
     -4083 (containter)
     <text>

至於這些container是什麼意思,便不去管那麼多了(抽得到文字便可以了)。但在處理PPDrawing時,有一些事項是必須加以注意的:

(1) -4092 container裡可能再含有-4093 container,對於這種多層次樹狀的文字方塊,不必往下層展開,只需展開第一層取文字即可(否則取到的文字並沒有顯示在PowerPoint上,用搜尋也找不到)。
(2) PowerPoint的群組物件闗係,是以-4093這個record type來組成的。也就是說,-4093裡還可能有-4093,形成層次式的群組結構關係,因此這裡的-4093 container是必須往下層展開的。

另外,母片中的title/body文字資料沒有意義,不必加以抽取,因此必須經由判斷TextHeaderAtom來判斷。TextHeaderAtom offset 0的長整數(4 byte),表示後面文字的意義:

0 = Title
1 = Body
2 = Notes
3 = Not Used
4 = Other (Text in a shape)
5 = Center body (subtitle in title slide)
6 = Center title (title in title slide)
7 = Half body (body in two-column slide)
8 = Quarter body (body in four-body slide)

因此除了4以外的情況都不必處理。

作者 : chiuinan2(青衫)討論區板主 Visual C++ .NET卓越專家VC++一代宗師Visual Basic優秀好手資訊類作業求救卓越專家一般曠世奇才程式設計甘苦談優秀好手C++ Builder優秀好手上班族的哈拉園地優秀好手C++頂尖高手Assembly優秀好手貼文超過3000則人氣指數超過150000點
[ 貼文 3732 | 人氣 170106 | 評價 34520 | 評價/貼文 9.25 | 送出評價 125 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2004/9/29 下午 09:27:42
現在說明一下備忘錄Notes record的Slide配屬問題。在Notes container裡都會有一個NotesAtom (type=1009),其offset 0的長整數,記錄的便是所屬的Slide ID,而這個Slide ID則記錄在SlideListWithText container裡的SlidePersistAtom offset 12的長整數(注意PowerPoint裡面有非常多的Slide ID,各有不同的意義)。不過一個Document container裡,可能有多個SlideListWithText container,這時必須檢查其次型態值,0表示notes slide,1表示master slide,2表示body slide。也就是說,我們要抓title/body文字時,應該到subtype=2的SlideListWithText container抓,如果是要取備忘稿(notes)的Slide ID,則必須到subtype=0的SlideListWithText container取。不同的subtype,取到的Slide ID是不一樣的。因此在剖析Document container時,我們必須先取到各個Slide對應於備忘稿的Slide ID,當遇到Notes container時,再抓取NotesAtom裡面的id進行搜尋,便能知道是屬於那個slide了。然而有些Slide是沒有備忘錄的,因此也必須判斷Slide裡面的SlideAtom,若offset 16的長整數(4 byte)若為0,表示無備忘稿,否則便是有,此時Notes的Slide ID才有意義。

PowerPoint檔的另一種較複雜結構(有點像是Word檔快速存檔的complex格式),會形成多個Document/Slide:

Document
Slide*
Notes*
Document
Slide*
Notes*
Document
Slide*
Notes*
...

第一個Document/Slide結構存的是最舊版本的資料,其後的結構為修改過程中新增/修改的部份。由於Document中主要是取title/body文字,且均一次存足,因此取時只需以最後一個為準。但Slide便不同了,必須判斷Slide/PPDrawing是否相同,才能決定要異動到那個Slide。然而我檢驗過Document和Slide各atom,並沒有相關的資訊,猜測應該是含在PPDrawing這個container內。而PPDrawing是Office Art File Format,找遍MSDN與微軟網站,甚至用Google都找不到相關文件,只好自己解解看。首先將PPDrawing展開,發現它只含一個"-4094"的container,繼續對這個container展開,得到:

type=-4094,size=1778
   type=-4088,size=8
   type=-4093,size=1674
     type=-4092,size=196
     type=-4092,size=272
     ...
   type=-4092,size=72

由於描述container屬性的,大都是第一個,因此觀察"-4088"這個atom裡面的值(8 byte剛好2個長整數), 可得知,第一個長整數表示"-4093"這個container裡有幾個"-4092"的container,第二個長整數似乎是某種ID,規律性目前不明。於是展開裡面的"-4092" container,得到:
 
type=-4092,size=196
   type=-4086,size=8
   type=-4085,size=48
   type=-4080,size=8
   type=-4083,size=100
     <text>

作者 : chiuinan2(青衫)討論區板主 Visual C++ .NET卓越專家VC++一代宗師Visual Basic優秀好手資訊類作業求救卓越專家一般曠世奇才程式設計甘苦談優秀好手C++ Builder優秀好手上班族的哈拉園地優秀好手C++頂尖高手Assembly優秀好手貼文超過3000則人氣指數超過150000點
[ 貼文 3732 | 人氣 170106 | 評價 34520 | 評價/貼文 9.25 | 送出評價 125 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2004/9/29 下午 09:28:31
由於<text>裡的文字,恰好是每個文字方塊的文字,因此"-4092" container,應該是用來儲存每個文字方塊的container,而"-4086"則應該是這個文字方塊的相關屬性。觀察裡面的值(包括比對異動的PPDrawing資料),發現第一個長整數便是這個文字方塊的ID。再回頭檢驗"-4088" atom,發現第二個長整數就是最大的文字方塊ID,而-4093裡第一個-4092裡的-4086 atom記錄的文字方塊ID,永遠是最小的。因此規則已經很明顯:

(1) 當產生一個PPDrawing container時,-4093裡會先產生一個虛擬的文字方塊,裡面的-4086則記錄整組文字方塊的起始ID。
(2) -4088裡會記錄文字方塊數,以及最後一個文字方塊ID(可能不存在被刪除了)。如果新增一個文字方塊,則其ID便是該ID+1。

也就是說,每個PPDrawing裡的文字方塊ID,必定介於第一個-4092裡-4086 atom的文字方塊ID,與-4088 atom所記錄的文字方塊ID之間。由於第一個-4092裡-4086 atom的文字方塊ID是起始ID,且不會變動,而Slide裡只會有一個PPDrawing container,因此這個ID便可以做為Slide的標誌,當ID相同時,便可直接將整個非title/body文字替換掉即可。

至於備忘錄的異動問題,由於後者應覆蓋前者,因此在決定備忘稿對應的Slide的同時,應將舊資料清除後再重抓備忘稿資料。另外還要考慮備忘稿刪除的情況,也就是遇到異動的Slide container時,仍要檢查SlideAtom offset 16的長整數值,若是0(沒有備忘稿),便要將舊資料清除掉。

作者 : chiuinan2(青衫)討論區板主 Visual C++ .NET卓越專家VC++一代宗師Visual Basic優秀好手資訊類作業求救卓越專家一般曠世奇才程式設計甘苦談優秀好手C++ Builder優秀好手上班族的哈拉園地優秀好手C++頂尖高手Assembly優秀好手貼文超過3000則人氣指數超過150000點
[ 貼文 3732 | 人氣 170106 | 評價 34520 | 評價/貼文 9.25 | 送出評價 125 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2004/9/29 下午 09:30:52
9. PowerPoint 95的格式

PowerPoint 95的檔案格式,基本上和97相同,一樣是container/atom的record結構,所不同的是,record header為16 byte,成為下列形式:

4 byte:record的形態值
4 byte:版本
4 byte:record內容的長度
4 byte:次形態值
? byte:reocrd內容

container/atom的結構組織也不同,整個資料是一個type=3的container,裡面再包一個Document container,所有的資料,包括title/body、文字方塊、母片等等,都是放在這個Document container裡。Document container裡有幾個List container(type=2000),必須藉由次形態值來辨識是那種串列,以便加以剖析讀取相關資料。以下便是我們所需要的List container(由於PowerPoint 95的許多形態值的意義和97並不相同,因此以下便直接以形態值代表container與atom):
 
(1) List container, subtype=10

裡面的Slide container便存放了各Slide的資料。Slide的結構如下:

Slide
   1008
     3000
     3010 (備忘稿資料)
   HeadersFooters (頁首頁尾資料)
   3000
     3010 (title/body資料)
     3010 (title/body資料)
     3008 (文字方塊資料)
     3008 (文字方塊資料)
     3008 (文字方塊資料)
     ...

注意後面3010 container裡的文字,可能是title,也可能是body,次序並不一定。檢驗的方法,便是取3010裡的3011 atom,offset 4的長整數若為0表示title,若為1表示body。至於實際文字資料放得很深,其位置便是3010或3008 container裡面的4001 container裡面的4002 container裡面的4064 container裡面的2003 atom。2003 atom是放文字的atom,其格式是ANSI字串,和97不同(95還沒支援Unicode)。 至於頁首頁尾的資料,則是在HeadersFooters container(type=4057)裡的CString atom(type=4026)。 要注意這個CString atom存的不是Unicode,而是ANSI字串。

(2) List container, subtype=11

裡面的MainMaster container儲放的是母片資料,其結構和Slide幾乎完全一模一樣。注意取時,應略過title/body資料。

作者 : chiuinan2(青衫)討論區板主 Visual C++ .NET卓越專家VC++一代宗師Visual Basic優秀好手資訊類作業求救卓越專家一般曠世奇才程式設計甘苦談優秀好手C++ Builder優秀好手上班族的哈拉園地優秀好手C++頂尖高手Assembly優秀好手貼文超過3000則人氣指數超過150000點
[ 貼文 3732 | 人氣 170106 | 評價 34520 | 評價/貼文 9.25 | 送出評價 125 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2004/9/29 下午 09:31:15
10. PowerPoint 4.0的格式

PowerPoint 4.0也是Ole物件檔,但它的資料串名稱不是"PowerPoint Document",而是"PP40"。這個資料串裡的資料內容,和PowerPoint 95以後的格式完全不同,並非採用record的形式。由於目前找不到任何相關的文件,因此解析起來頗為困難。以下是我解析的檔頭結構(共計84h byte):

4 byte:固定為ED DE AD 0B
8 byte:不明
2 byte:1748h開始的資料數 (假設是n)
2 byte:不明
4 byte:整體資料長度
? byte:不明

從84h開始,應該是字型相關的資訊,長度固定。從1748h開始,應該是字型參用的相關資訊,但長度不定,實際長度為檔頭0Ch的短整數*8。也就是說,實際資料是從1748h+n*8的地方開始。實際資料裡的格式,是一個Slide接著一個Slide,依序置放,然而各Slide裡的資料格式與意義頗為難解,同時也不想花太多時間在這地方,因此目前只抓出文字區塊的規律如下:

00 FF FF 64 00 00 00 00 00 ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? 00 00 01 00 size 00 00 content

其中??表示可為任何數字,size為2 byte的短整數,content的字串格式為ANSI。也就是說,我們只需針對整個檔案資料逐一比對,找出符合上述位元組資料順序者,便可以找到各slide的文字資料。雖然簡單,但也有一些副作用,那就是一些隱藏在PowerPoint裡的文字都會被抓出來,例如"Click here to edit master slide"等字串。由於我們略掉了很多關於文字塊屬性作用的資料,因此並無法判斷這個文字塊的文字資料是否真的要顯示,目前也只能暫時都將之抓取出來建索引。

作者 : chiuinan2(青衫)討論區板主 Visual C++ .NET卓越專家VC++一代宗師Visual Basic優秀好手資訊類作業求救卓越專家一般曠世奇才程式設計甘苦談優秀好手C++ Builder優秀好手上班族的哈拉園地優秀好手C++頂尖高手Assembly優秀好手貼文超過3000則人氣指數超過150000點
[ 貼文 3732 | 人氣 170106 | 評價 34520 | 評價/貼文 9.25 | 送出評價 125 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2004/9/30 下午 02:17:03
11. Excel 97/95的格式

Excel 97的文件內容,主要是存放在"Workbook"的資料串裡面(Excel 95則是"Book"資料串,其內容和97完全相同),因此底下便針對這個資料串加以剖析說明。

"Workbook"這個資料串,主要是由record所組成,但它和PowerPoint不同的是,整個record的結構為一扁平結構,而非樹狀結構。record的形式如下:

2 byte:形態值
2 byte:record內容長度
? byte:record內容

至於record的組織結構,主要以BOF(type=0809h)與EOF(type=000Ah)做為分隔,也就是:

BOF
<公用資料>
EOF
BOF
<Sheet資料>
EOF
BOF
<Sheet資料>
EOF
...

當EOF之後不是緊接著BOF時,表示整個檔案結束。在公用資料區裡,比較重要的有下列幾個record:

(1) type = 85h (BOUNDSHEET: Sheet Information) - 記錄了各Sheet的相關資料,其格式為:

4 byte:該Sheet資料的起始位置(相對於資料串,位置一開始必是BOF record)
1 byte:Sheet形態, 其值如下:
0 = worksheet or dialog sheet
1 = Microsoft Excel 4.0 macro sheet
2 = chart
6 = Visual Basic module
1 byte:Sheet旗標, 其值如下:
bit 0-1:Hidden狀態, 0=可見, 1=隱藏, 2=絕對隱藏(只能用VBA方式清除)
bit 2-7:保留
2 byte:Sheet名稱的長度(若是"Book"資料串,則只有1 byte)
? byte:Sheet名稱

(2) type = FCh (SST: Shared String Table) - 記錄了各Sheet使用的字串,其格式為:

4 byte:Shared String Table與Extended String Table的字串總數
4 byte:Shared String Table的字串總數
? byte:字串陣列

必須注意的是,Excel的字串格式頗特別,記錄方式如下:

2 byte:字串的字數
1 byte:字串資料旗標
bit 0 (01h):0表示字串資料的Unicode高位元組都是0,只存低位元組的資料
bit 2 (04h):1表示有Extended String(記錄語系相關資訊)
bit 3 (08h):1表示有Rich String(記錄字型/字色等資訊)
2 byte:formatting runs的數目(有Rich String時才有這個資料,實際資料大小=數目*4 byte)
4 byte:Extended String的資料大小(有Extended String時才有這個資料)
? byte:字串資料
? byte:formatting runs資料
? byte:Extended String資料

其中我們需要的,只有字串資料而已,其他的資料可以不用管它。

也就是說,在取得Excel資料前,我們必須剖析完公用資料區,得知有那些Sheet要處理,以及整個共享字串表,然後才能處理Sheet資料。不過共享資料表的資料可能很多,而Excel每個record的大小有一定的限制,這時共享資料表的資料會被切成數個record,也就是在SST record後面會接了許多的Continue record(type=3Ch),而Continue record裡的資料便是SST被切出來的資料。因此在讀取SST時,必須考慮此一情形。由於切開的點可能在任何位置,在處理時頗為麻煩。也許有人認為,只需將所有資料全部讀取接合後,再一併處理即可,但這是錯的。當切開的點是字串資料時,Excel會在Continue record的第一個資料裡多放一個字串資料旗標,以便將剩餘的字串資料做做佳的編碼方式(亦即換個record後,同一字串的編碼方式可能改變了),因此不能全部接合後再處理。總之,這個部份需要特別處理才行。

另外,如果公用資料區裡遇到FILEPASS record(type=2Fh),表示其後的record內容是加密過的,必須經過解密才能讀取。由於不知Excel的加密方法,因此目前並不處理(活頁簿保護時會產生此一現象)。由於Excel檔仍可開啟且不必輸入密碼,因此容易讓使用者誤以為我們沒將它解出來,但目前也沒有什麼好方法可處理。
作者 : chiuinan2(青衫)討論區板主 Visual C++ .NET卓越專家VC++一代宗師Visual Basic優秀好手資訊類作業求救卓越專家一般曠世奇才程式設計甘苦談優秀好手C++ Builder優秀好手上班族的哈拉園地優秀好手C++頂尖高手Assembly優秀好手貼文超過3000則人氣指數超過150000點
[ 貼文 3732 | 人氣 170106 | 評價 34520 | 評價/貼文 9.25 | 送出評價 125 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2004/9/30 下午 02:17:49
要取得各Sheet的文字資料,有兩種方法,一種是將record全部掃過一遍(較慢),一種是利用INDEX/DBCELL/ROW的結構快速取得。然而Excel是允許檔案中無INDEX/DBCELL/ROW結構,為了避免出問題,我們還是採用全部掃過一遍的方式處理。對於一個Sheet資料而言,Cell的內容是記錄在下面record中的:

(1) type = FDh (LABELSST: String Constant/SST) - 記錄文字串(在共享資料表裡)的Cell,格式為:

2 byte:第幾行(由0編起)
2 byte:第幾列(由0編起)
2 byte:XF record索引值
4 byte:共享字串表的索引值

(2) type = 204h (LABEL: String Constant) - 記錄文字串的Cell(舊版),格式為:

2 byte:第幾行(由0編起)
2 byte:第幾列(由0編起)
2 byte:XF record索引值
2 byte:字串byte數
? byte:ANSI字串

(3) type = D6h (RSTRING: Character Formatting) - 記錄文字串的Cell(舊版),格式為:

2 byte:第幾行(由0編起)
2 byte:第幾列(由0編起)
2 byte:XF record索引值
2 byte:字串byte數
? byte:ANSI字串
? byte:formatting資料

(4) type = 27Eh (RK: RK Number) - 記錄數字的Cell,格式為:

2 byte:第幾行(由0編起)
2 byte:第幾列(由0編起)
2 byte:XF record索引值
4 byte:RK數值(後述)
 
(5) type = BDh (MULRK: Multiple RK Cells) - 記錄數字(多個連續列)的Cell,格式為:

2 byte:第幾行(由0編起)
2 byte:起始列(由0編起)
? byte:RK Cell資訊(每個6 byte)
2 byte:XF record索引值
4 byte:RK數值(後述)
2 byte:結束列(由0編起)

(6) type = 203h (NUMBER: Floating-Point Number) - 記錄浮點數的Cell,格式為:

2 byte:第幾行(由0編起)
2 byte:起始列(由0編起)
2 byte:XF record索引值
8 byte:IIIE 8 byte浮點數(後述)
 
(7) type = 6h (FORMULA: Cell Formula) – 記錄公式的Cell,格式為:

2 byte:第幾行(由0編起)
2 byte:起始列(由0編起)
2 byte:XF record索引值
8 byte:IIIE 8 byte浮點數,若公式有誤,或公式計算結果是字串/布林值時,最高2 byte為FFFFh。字串結果會記錄在緊跟著的STRING record裡。
2 byte:旗標值 (略)
? byte:計算公式字串資料

(8) type = 207h (STRING: String Value of a Formula) - 記錄公式結果(字串),格式為:

? byte:字串資料(參見前面的字串格式說明)

作者 : chiuinan2(青衫)討論區板主 Visual C++ .NET卓越專家VC++一代宗師Visual Basic優秀好手資訊類作業求救卓越專家一般曠世奇才程式設計甘苦談優秀好手C++ Builder優秀好手上班族的哈拉園地優秀好手C++頂尖高手Assembly優秀好手貼文超過3000則人氣指數超過150000點
[ 貼文 3732 | 人氣 170106 | 評價 34520 | 評價/貼文 9.25 | 送出評價 125 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2004/9/30 下午 02:18:27
以下開始解釋幾個Excel數值的編碼方式:
 
(1) RK數值格式

最低的2 bit做為數值形態,表示最高的30 bit數值意義:

0 = IEEE浮點數(後述)
1 = IEEE浮點數*100(也就是解出來的值要再除以100)
2 = 整數
3 = 整數*100

(2) IEEE浮點數(30 bit)

最高bit用以代表負數,接下來的11 bit為exponent,最後的18 bit為mantissa。exponent由3FFh開始表示0次方(400h=1次方、3FEh=-1次方)。mantissa注意只記錄小數部份(整數部份必須為1)。唯一例外的是,整個所有bit若是0,則表示數字0。不懂的話,請去看計算機概論裡相關的說明。

(3) IEEE浮點數(8 byte,64 bit)

和30 bit格式相同,只是mantissa由18 bit擴增至52 bit而已(精準度增加)。

至於XF record索引值,係指向該Cell的format record(type=E0h,較舊的版本type=43h),這些record是放在公用資料區裡。如果索引值<=49,表示係Excel的內定顯示格式。由於顯示格式眾多(50個內定格式+其他user自定格式),要一一處理起來很麻煩,而Excel裡的數字資料裡通常不會被用來檢索,因此目前數字形態的資料,便不加以處理,直接略過(若確實有需要再說)。

最後要說明的是Cell裡面的註解,它是放在Sheet資料的TXO record(Text Object,type=1B6h),每個註解一個TXO record。其格式如下:

2 byte:旗標
2 byte:文字走向
6 byte:保留
2 byte:文字字數
2 byte:formatting runs數目
4 byte:保留

實際文字是記錄在緊跟著TXO record的第一個Continue record,裡面的字串資料不含文字字數(已記錄在TXO record中)。

作者 : chiuinan2(青衫)討論區板主 Visual C++ .NET卓越專家VC++一代宗師Visual Basic優秀好手資訊類作業求救卓越專家一般曠世奇才程式設計甘苦談優秀好手C++ Builder優秀好手上班族的哈拉園地優秀好手C++頂尖高手Assembly優秀好手貼文超過3000則人氣指數超過150000點
[ 貼文 3732 | 人氣 170106 | 評價 34520 | 評價/貼文 9.25 | 送出評價 125 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2004/9/30 下午 02:19:03
12. BIFF4的格式

前面提到的Excel 97/95檔案格式(也稱為BIFF格式),可適用於下列版本:
 
BIFF5 - Microsoft Excel version 5
BIFF7 - Microsoft Excel 95(亦稱為Microsoft Excel version 7)
BIFF8 - Microsoft Excel 97

然而BIFF4以前的版本並非Ole檔,而格式也稍有不同,必須特別另行處理。record的形式如下:

1 byte:形態值
1 byte:次形態值
2 byte:record內容長度
? byte:record內容

由於BIFF4只有一個Sheet,因此沒有BOF/EOF的結構,也沒有公用資料區,其第一個record固定是type=9,然後一直剖析到檔案結束為止。以下是幾個需要處理的record:

(1) type = 4,存放字串資料,格式為:

次形態值=0時:

2 byte:第幾行(由0編起)
2 byte:起始列(由0編起)
3 byte:不明
1 byte:字串byte數
? byte:ANSI字串

次形態值<>0時:

2 byte:第幾行(由0編起)
2 byte:起始列(由0編起)
2 byte:不明
2 byte:字串byte數
? byte:ANSI字串

(2) type = 2,存放短正整數資料,格式為:

2 byte:第幾行(由0編起)
2 byte:起始列(由0編起)
3 byte:不明
2 byte:短正整數

(3) type = 3,存放浮點數資料,格式為:

2 byte:第幾行(由0編起)
2 byte:起始列(由0編起)
3 byte:不明
8 byte:IEEE 8 byte浮點數

至於數字的顯示格式,目前不處理。

13. Embeded Object格式
作者 : chiuinan2(青衫)討論區板主 Visual C++ .NET卓越專家VC++一代宗師Visual Basic優秀好手資訊類作業求救卓越專家一般曠世奇才程式設計甘苦談優秀好手C++ Builder優秀好手上班族的哈拉園地優秀好手C++頂尖高手Assembly優秀好手貼文超過3000則人氣指數超過150000點
[ 貼文 3732 | 人氣 170106 | 評價 34520 | 評價/貼文 9.25 | 送出評價 125 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2004/9/30 下午 02:20:00
13. Embeded Object格式

Embeded Object的格式,會隨著Embeded所在的軟體,以及要Embeded進去的物件的不同,而有許多差異。首先我們先來看看在Notes文件的Embeded Object格式(只看重要資料串的目錄結構即可):

(1) 在Notes裡Embeded一個文字檔或zip檔

<Root Entry>
    <>
     <\001Ole10Native>

原始的檔案資料便是放在"\001Ole10Native"這個資料串裡(第一個byte是ASCII 01字元),裡面的資料格式為:

整個資料串大小(long)+2 byte+文件顯示名稱+文件處理程式名稱+4 byte+全路徑檔名長度+全路徑檔名+檔案大小(long)+檔案內容

這裡的名稱和檔名均採用ASCIIZ格式,也就是以ASCII 00字元為結束。由此可簡單抽出原來的Embeded檔案內容。然而有些非檔案型的資料也是放在"\001Ole10Native"資料串裡,因此在抽取時,必須要判斷檔名是否合法,若是非法表示它Embeded的不是一個檔案。

(2) 在Notes裡Embeded一個word檔

<Root Entry>
    <Word.Document.8>
     <ObjectPool>
     <WordDocument>

我們再看一下Word檔的資料串目錄結構:

<Root Entry>
    <WordDocument>
    <ObjectPool>

也就是說,Word檔被Embeded之後,整個目錄的資料串都被往下移一層了(以目錄/檔案結構的眼光來看,這是理所當然的事)。

綜合上述,在非Ole物件軟體裡的Embeded Object,其內容為:

<Root Entry>
    <物件辨識名稱>
     物件資料

以純檔案而言,物件辨識名稱為空字串,以Office檔案而言,辨識名稱便是Word.Document.?、 PowerPoint.Show.?、Excel.Sheet.?,其中?是一個數字,表示該Office物件資料的版本(註:由於Office的Ole物件名稱有很多種,例如Word.Picture.?,因此最好只檢查前面的部份字串)。要解這類的Embeded Object,只需辨識主目錄名稱,再決定如何處理即可。其中必須注意的是PDF檔,若以一般檔案插入時(也就是封裝式的物件),其格式和文字檔或Zip檔是相同的,但若是以新建物件方式插入時(必須有PDF Writer時才有此一功能),則Embeded的結果會變成和Office類似的形式:

<Root Entry>
<AcroExch.Document>
    <Contents>

此時Contents裡記錄的便是PDF的原始資料。注意其他Ole物件也可能使用Contents資料串,因此如果沒有物件辨識名稱,而只以Contens資料串來辨識是否為PDF檔時,必須再檢查檔頭是否為”%PDF-“才行。

作者 : chiuinan2(青衫)討論區板主 Visual C++ .NET卓越專家VC++一代宗師Visual Basic優秀好手資訊類作業求救卓越專家一般曠世奇才程式設計甘苦談優秀好手C++ Builder優秀好手上班族的哈拉園地優秀好手C++頂尖高手Assembly優秀好手貼文超過3000則人氣指數超過150000點
[ 貼文 3732 | 人氣 170106 | 評價 34520 | 評價/貼文 9.25 | 送出評價 125 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2004/9/30 下午 02:20:37
接著再來看一下Word Embeded Object後的資料串結構:

(1) Embeded一個純文字檔

<Root Entry>
    <WordDocument>
    <ObjectPool>
     <_1132414719>
     <\001Ole10Native>

(2) Embeded一個word檔

<Root Entry>
    <WordDocument>
    <ObjectPool>
     <_1132414639>
     <ObjectPool>
     <WordDocument>

由此即可看出,Word的Embeded Object都是放在ObjectPool這個目錄底下,同時各Object均還賦予一個特殊編號的子目錄,實際資料就放在該子目錄裡,其格式和之前提的Notes Embeded Object格式沒什麼兩樣。知道它的結構後,要將資料解出來就簡單了。以下則是Excel的狀況:

(1) Embeded一個純文字檔

<Root Entry>
    <MBD04419A08>
     <\001Ole10Native>
    <Workbook>

(2) Embeded一個word檔

<Root Entry>
    <MBD044182E4>
     <ObjectPool>
     <WordDocument>
    <Workbook>

亦即每個物件都是放在與"Workbook"資料串同一層,其子目錄均以MBD做為開頭。

至於PowerPoint的Embeded Object比較麻煩,實際的物件資料並不是放在IStorage的目錄檔案結構裡,而是自行放在"PowerPoint Document"資料串裡的ExOleObjStg record中(type=4113),其內容為:

4 byte:OleID(沒有用處)
? byte:壓縮的Ole物件資料

注意在ExOleObjStg裡的物件資料是有壓縮的,其壓縮法便是ZIP檔中的LZW壓縮,因此可利用zlib裡的inflate函數來解壓。解壓後的資料,便是原來的Office檔(若是文字檔等,則是Ole的封裝格式,把它想成是Notes Embeded Object拿掉第二層目錄就對了)。由於PowerPoint的Embeded Object是放在文件中,自然也需要考慮到Ole物件的異動狀況並加以處理。最簡單的方法,便是從Document record裡的ExObjList container(type=1033)中找出各物件資料的所在位置,並加以讀出處理即可。各物件資料的所在位置,是放在ExEmbed container(type=4044)裡的ExOleObjAtom中(type=4035)的第5個長整數(offset=16)。不過這個數值記錄的是一個索引值,必須參用到PersistPtr才能得到Ole物件資料實際所在的ExOleObjStg atom位置。PersistPtr是放在PersistPtrIncrementalBlock atom裡(type=6002),其內容為:

位置數目與起始編號:長整數,最高12 bit為數目,最低20 bit為起始編號(即PersisPtr的索引值,由0編起)
檔案位置:均為長整數,讀取後置入PersisPtr中
位置數目與起始編號
檔案位置


作者 : chiuinan2(青衫)討論區板主 Visual C++ .NET卓越專家VC++一代宗師Visual Basic優秀好手資訊類作業求救卓越專家一般曠世奇才程式設計甘苦談優秀好手C++ Builder優秀好手上班族的哈拉園地優秀好手C++頂尖高手Assembly優秀好手貼文超過3000則人氣指數超過150000點
[ 貼文 3732 | 人氣 170106 | 評價 34520 | 評價/貼文 9.25 | 送出評價 125 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2004/9/30 下午 02:21:07
由於PersistPtrIncrementalBlock atom會隨著編輯的過程而累增,因此必須讀取整個資料串中所有的PersistPtrIncrementalBlock atom並加以整合,才能得到完整的PersistPtr。綜合上述,要取得PowerPoint檔裡的Ole物件,其程序為:

(1) 處理所有PersistPtrIncrementalBlock atom,得到PersisPtr值
(2) 尋找現行Document container裡的ExObjList container,由其內的ExEmbed container裡的ExOleObjAtom得到各Ole物件資料的PersisPtr索引值,再參照PersisPtr得到實際所在的位置
(3) 到Ole物件資料所在的位置,取得ExOleObjStg atom,將裡面的物件資料解壓,便是實際的Ole物件內容了

最後再來看一下RTF的Embeded Object格式。當Word資料存成RTF檔時,其中的Embeded Object會存在\object命令裡,實際資料則在\objdata命令裡。\objdata後面會接著一串16進位數字字串,將這些數字字串還原成Binary的形式,再解析其格式如下:

8 byte:不明
4 byte:物件辨識名稱長度
? byte:物件辨識名稱,ANSI字串,純檔案為Package,Office檔仍為Word.Document.?之類的名稱
8 byte:不明
? byte:實際物件資料

實際物件資料,若是純檔案(如文字檔、zip檔,則為"\001Ole10Native"資料串的資料,否則為下列格式:

4 byte:物件資料byte數
? byte:Ole檔資料(將它存檔起來就是完整的Office檔案)

作者 : chiuinan2(青衫)討論區板主 Visual C++ .NET卓越專家VC++一代宗師Visual Basic優秀好手資訊類作業求救卓越專家一般曠世奇才程式設計甘苦談優秀好手C++ Builder優秀好手上班族的哈拉園地優秀好手C++頂尖高手Assembly優秀好手貼文超過3000則人氣指數超過150000點
[ 貼文 3732 | 人氣 170106 | 評價 34520 | 評價/貼文 9.25 | 送出評價 125 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2004/9/30 下午 02:23:00
終於全部po完了, 不算短的一篇文章. 不過點擊率似乎不太高, 也許大家對檔案格式的議題比較沒什麼興趣吧...
作者 : passerx(passer)
[ 貼文 129 | 人氣 2029 | 評價 400 | 評價/貼文 3.1 | 送出評價 2 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2004/10/1 下午 10:16:42

>終於全部po完了, 不算短的一篇文章. 不過點擊率似乎不太高, 也許大家對檔案格式的議題比較沒什麼興趣吧...

你要不要參考一下open office的東西重新寫一份比較完整的,
而不是第幾個offset的什麼數字大概是什麼東西,
我想這樣有興趣的人會比較多一點吧!
作者 : tourist(笨阿Q)
[ 貼文 8 | 人氣 251 | 評價 0 | 評價/貼文 0 | 送出評價 3 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2008/5/16 上午 12:46:58
感謝先進提供的資料,讓我增長了知識。
作者 : wsszsmx(吻蛇起舞)
[ 貼文 2 | 人氣 0 | 評價 0 | 評價/貼文 0 | 送出評價 0 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2008/8/19 下午 06:34:12
写;的不错; 我在http://bbs.intohard.com论;坛;上看过;一篇《修复复合文档;文件头;破坏的修复》
但看了您写;的这;篇文章很不错;!如果档;案头;和FAT表都被破坏 就没;有修复的意义;了

想看您写;的文章 是否可以继;续;深入些...........

谢;谢;>>>>>>>.
作者 : wsszsmx(吻蛇起舞)
[ 貼文 2 | 人氣 0 | 評價 0 | 評價/貼文 0 | 送出評價 0 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2008/8/19 下午 06:43:57
但小資料磁區裡的資料實際上在那裡呢?其實就是根目錄裡的資料串。這也就是為什麼根目錄的資料,一律都是放在標準512 byte磁區裡。而在根目錄資料串裡,便是以64 byte為一單位,切割成小磁區,供小資料存放使用。於是, 要取得一個資料串的過程,便成為:

(1) 在檔案目錄結構裡,找出該資料串相同名稱的屬性。屬性裡的資料磁區指標和大小,便是資料所在的位置。
(2) 如果資料串的資料,是在標準512 byte磁區中,便到FAT表裡找出該資料串使用的磁區,一個一個依次載入。注意OLE檔可能不是剛好512 byte,如果是最後一個sector,必須只讀取檔案長度剩餘的部份。
(3) 如果資料串的資料,是在小資料儲存區中,便必須載入根目錄的整個資料串,然後到小資料儲存區FAT表裡,找出該資料串使用的小資料磁區,再一個一個從根目錄資料串的小磁區裡載入。

这;个;怎么看不懂?
在根目錄資料串裡,便是以64 byte為一單位,切割成小磁區,供小資料存放使用。这;个;明白,怎么供小資料存放使用?
 板主 : Jammy , simula
 > 一般討論區 - 討論區
 - 最近熱門問答精華集
 - 全部歷史問答精華集
 - 一般討論區 - 知識庫
  ■ 全站最新Post列表
  ■ 我的文章收藏
  ■ 我最愛的作者
  ■ 全站文章收藏排行榜
  ■ 全站最愛作者排行榜
  ■  月熱門主題
  ■  季熱門主題
  ■  熱門主題Top 20
  ■  本區Post排行榜
  ■  本區評價排行榜
  ■  全站專家名人榜
  ■  全站Post排行榜
  ■  全站評價排行榜
  ■  全站人氣排行榜
 請輸入關鍵字 
  開始搜尋
 
Top 10
評價排行
一般討論區
1 青衫 5370 
2 HKLN.net 1370 
3 冼鏡光 650 
4 simula 610 
5 joe 560 
6 DEMO999 520 
7 小朱 490 
8 jonay 480 
9 BlueTulip 460 
10 Jammy 370 
一般討論區
  專家等級 評價  
  一代宗師 10000  
  曠世奇才 5000  
  頂尖高手 3000  
  卓越專家 1500  
  優秀好手 750  
Microsoft Internet Explorer 6.0. Screen 1024x768 pixel. High Color (16 bit).
2000-2019 程式設計俱樂部 http://www.programmer-club.com.tw/
0.171875