top
Loading...
Paradox的數據表損壞后如何來修復

Paradox數據庫是一種桌面型數據庫,具有有效性檢查、參考完整性、口令保護、字段類型豐富等特點。但是在實際使用過程中,當數據庫引擎出現問題時,由于數據沒有全部寫入數據庫中,可能造成數據庫損壞。本文介紹Paradox數據庫的數據表即DB文件損壞時的修復辦法。

DB文件格式

Paradox數據表的損壞可分為兩種情況:一是數據表表頭損壞,數據根本讀不出來;二是數據塊鏈表出現問題,只能看見部分記錄。無論是上述哪種情況,只要DB文件中的記錄仍然存在(本文稱這種損壞為邏輯損壞),就有可能恢復數據。

要恢復數據,必須了解DB文件的格式。DB文件保存著一個數據表的數據記錄,DB文件里的第一個塊是表頭(塊),表頭后面跟著數據塊。DB文件的邏輯結構如下圖所示:

其中表頭(表頭中部分字段的描述如表1所示)包含第一個數據塊、最后一個數據塊和第一個自由塊的塊號。每個塊都有編號,在表頭后面的第一個塊號碼是1。全部塊(表頭除外)都有相同的大小,這意味著塊號能用來計算塊的偏移量:

塊偏移=塊長度×(塊號碼-1)+表頭長度

數據塊分塊頭和記錄兩部分,其中塊頭由數據塊的前6個字節組成。數據塊使用雙向鏈表進行組織,鏈表元素(或稱為節點)是塊頭的前4個字節,包含了下一個數據塊和前一個數據塊的塊號。在數據塊內,記錄順序存儲,記錄間沒有空隙,數據塊的塊頭中還包含了當前塊中最后一個記錄的偏移量。表2是數據塊格式描述。

其中最后一個記錄的偏移量和塊頭部的末端相關,并以塊號加6計算偏移量。

分析問題

有了表的結構知識,我們就可以用它來分析數據表損壞的原因,從而找出修復數據表的方法。

當數據表邏輯損壞時,如果數據庫引擎報告表索引頭錯誤,通過分析可知多半是表頭中有關塊或記錄的參數不匹配,比如“使用的塊數”大于“總的塊數”,或是記錄數和塊數不匹配。如果只能顯示部分數據,可能是表頭中“總的記錄數”小于實際記錄數,也可能是數據塊鏈表錯亂。

如果只是表頭索引參數出了問題,我們可以通過分析表頭相關信息,用16進制編輯器直接進行參數修改就可解決問題;如果是數據塊鏈表出了問題,一般無法直接修改(比如對有成千上萬條記錄的數據表)。

筆者的方法是采用冗余方式計算鏈表,即數據塊和記錄都按最大數進行處理。每個數據塊中記錄的最大數跟數據塊大小和記錄長度有關,其關系為:記錄數(max)=[數據塊大小÷記錄長度]([]表示取運算結果的整數部分);而數據塊的多少又跟DB文件大小有關,其關系為:數據塊數(max)=(文件大小-塊頭大小)÷數據塊大小(這一運算結果應為整數,如果不為整數則取不小于它的最小整數)。以計算出來的兩個數據修改表頭索引參數,然后再根據表頭參數將鏈表修復為順序結構鏈表。到此為止,可以說基本上修復了數據表。不過,這樣一來表中就可能出現重復數據或無用數據,因此還要對數據表進行排序、刪除重復記錄等處理,然后就可以得到一個比較完美的數據表。

編程實現

下面給出用C++ Builder編程來修改表頭索引和數據塊鏈表的主要源代碼。需要注意的是,該方法不適用于有密碼的數據表。

用C++ Builder 新建一個工程,在窗體上創建一個OpenDialog1組件和一個Button1組件,并將OpenDialog1的Filter屬性設置為“*.DB|*.DB”,然后在Button1的OnClick事件中加入以下代碼:

void   __fastcall   TForm1::Button1Click(TObject   *Sender)       {       if(OpenDialog1->Execute())       {       //變量聲明       AnsiString   fn;       unsigned   char   fd[65];       int   rl,lhb,dbs,nr,tb;//rl為記錄長度,lhb為表頭塊長度,dbs為數據塊大小,nr為記錄數,tb為總的數據塊數       unsigned   long   sof;       struct   stat   statbuf;       fstream   DBFile;       //獲取待修改數據表名       fn=OpenDialog1->FileName;       //獲得文件大小,以此計算數據表中最大的塊數       int   handle;       handle=open(fn.c_str(),O_RDONLY);       fstat(handle,   &statbuf);       sof=statbuf.st_size;       close(handle);       //打開文件并將表頭信息讀取到數組fd中       DBFile.open(fn.c_str(),ios::in|ios::out|ios::binary);       DBFile.read(fd,64);       //計算總的塊數       dbs=fd[5]*1024;       lhb=fd[2]+fd[3]*256;       tb=ceil((sof-lhb)/dbs);       //根據總塊數計算總的記錄數       rl=fd[0]+fd[1]*256;       nr=dbs/rl;       nr=nr*tb;       //修改總的記錄數       fd[6]=nr%256;       fd[7]=nr/256;       fd[8]=nr/(256*256);       fd[9]=nr/(256*256*256);       //修改數據塊數       fd[10]=fd[12]=fd[16]=fd[58]=tb%256;       fd[11]=fd[13]=fd[17]=fd[59]=tb/256;       fd[14]=1;       fd[15]=0;       //將相關信息寫到文件中       DBFile.seekg(0,0);       DBFile.write(fd,64);       //根據表頭信息修復數據塊鏈表       if(tb>1)       for(int   i=0;i

(T114)

作者:http://www.zhujiangroad.com
來源:http://www.zhujiangroad.com
北斗有巢氏 有巢氏北斗