常用的壓縮工具(如 WinZip 或 ARJ)現(xiàn)在也不起作用,因為我們并不是對整個數(shù)據(jù)庫進行壓縮(那樣光盤上的數(shù)據(jù)將無法檢索),而只是要將數(shù)據(jù)庫中的某些字段的內(nèi)容壓縮后存入庫中——以減少整個數(shù)據(jù)庫占用的空間——然后在使用中動態(tài)解壓將數(shù)據(jù)還原為本來面目。 
  
                                                                                              最理想的辦法當然是數(shù)據(jù)庫系統(tǒng)(DBMS)本身直接支持數(shù)據(jù)壓縮存儲,但令人遺憾的是:常見的DBMS均未提供該功能。 
  
  在互聯(lián)網(wǎng)上確實能找到一些有關數(shù)據(jù)壓縮的思想、算法甚至 C語言的部分源代碼,但它們大都過于復雜,或應用范圍有限(如僅對圖像或純數(shù)字數(shù)據(jù)有效),或是在版權方面有太苛刻的要求。 
  
  最后我們采用了自行設計的算法。該方法的壓縮率只有 20%至25% ,但它小巧、輕易實現(xiàn),在實際應用中取得了良好的效果。 
  
  一、算法概述 
  壓縮冗余的比特位(bit)是常見的壓縮思想之一。 
  例如,字符串 CCW-2000 的二進制表示為: 
  01000011,01000011,01010111,00101101,00110010,00110000,00110000,00110000 
  其中每個二進制的前導“0”是冗余的,去掉前導“0”后的表示為: 
  1000011,1000011,1010111,101101,110010,110000,110000,110000 
  
  這顯然達到了數(shù)據(jù)壓縮的目的,但同時也帶來一個很大的問題:二進制流僅僅由“0”和“1”組成,并不存在上面為了表述清楚而加入的“,”。 即:由于壓縮后的二進制不再是“定長”的,兩個二進制之間如何“劃界”成了難題。 
  
  常見的解決方案有:霍夫曼表(Huffman codes)、LZW的動態(tài)查詢表、Markov的多維長度字典表,以及根據(jù)前值和前長度變換應用規(guī)則的DAKX方法等。但這些方法大都過于繁雜或未公布技術細節(jié)而難以實現(xiàn)。 
  
  為了實現(xiàn)起來簡捷,避免用“宰牛刀”,我們設計的算法采用一種折衷的思想:用“縮短”了的“定長”二進制來實現(xiàn)壓縮,同時又避免了“劃界”的難題。 
  
  具體思路是: 
  (1) 所有字符的二進制長度均為 6個 bit而不是一般的 8個 bit。即每個字符節(jié)約 2個 bit。這決定了本方法的理想(最高)壓縮率為 25%。 
  (2) 由于 6個 bit僅能區(qū)分64種字符,故將內(nèi)碼小于 127的字符分為“常用”和“不常用”兩組。 
  (3) 第一組由英文字母(大小寫)、0至9的數(shù)字、空格以及一個控制字符 
  
  組成;第二組由內(nèi)碼小于 127的其它字符組成。 
  
  (4) 每組內(nèi)的字符均重新編碼,以使其“新內(nèi)碼”均小于64。 
  (5) 第二組的每個字符前均以第一組中的控制字符做前綴。 
  (6) 內(nèi)碼大于等于 127的字符用普通的 8-bit表示,前綴是兩個連續(xù)的第一組中的控制字符。 
  (7) 本方法顯然僅適用于西文文本。 
  (8) 壓縮后的數(shù)據(jù)相當于被加密了,這大大提高了信息安全性。 
  
  二、算法實現(xiàn) 
  我們制作的西歐進出口商數(shù)據(jù)庫中有大量大段的企業(yè)英文描述,故應用上述算法非常成功,如愿以償?shù)貙⒄麄€系統(tǒng)的大小降到了 600兆以下,可以輕松地裝入一張光盤中。 
  
  檢索系統(tǒng)用VB編程,數(shù)據(jù)庫用的是 MS-access。 
  
  后附程序為清楚起見,將壓縮數(shù)據(jù)庫的功能改為壓縮文本文件的功能。該程序已在機器上測試通過,可作為一個壓縮工具單獨運行(為了同時說明壓縮及解壓縮這兩個函數(shù)的使用,下面的程序會將剛被壓縮的文件再進行解壓縮,解壓后文件與源文件是完全一樣的)。 
  
  附完整的VB源代碼: 
  Option EXPlicit 
  Dim comped$, comping As String * 1, comping_p%, bit_p& 
  Dim ebitmask(1 To 8) As Integer ' 存儲掩碼的數(shù)組 
  Sub Command1_Click () 
  Dim ibuf$, obuf1$, obuf2$ 
  MousePointer = 11 
  ' 設置掩碼 
  ebitmask(1) = 128: ebitmask(2) = 64: ebitmask(3) = 32: ebitmask(4) = 16 
  ebitmask(5) = 8: ebitmask(6) = 4: ebitmask(7) = 2: ebitmask(8) = 1 
  Open "d:/temp/comPRess/theory.txt" For Input As #1 ' 壓縮前的源文件 
  Open "d:/temp/compress/theory.6BT" For Output As #2 ' 壓縮后的文件 
  Open "d:/temp/compress/theory_2.txt" For Output As #3 ' 解壓后的文件 
  Do While Not EOF(1) 
  Line Input #1, ibuf 
  obuf1 = DoCompress(ibuf) 
  Print #2, obuf1 
  obuf2 = UnDoCompress(obuf1) 
  Print #3, obuf2 
  Loop 
  Close #1, #2, #3 
  MousePointer = 0 
  End Sub 
  Function DoCompress (in_buf$) As String ' 對輸入的字符串進行壓縮 
  Dim i&, buf_len&, c As String * 1 
  comped = "": comping = Chr(0): comping_p = 0 
  buf_len = Len(in_buf) 
  If buf_len > 0 Then 
  For i = 1 To buf_len 
  c = Mid(in_buf, i, 1) 
  Select Case c 
  Case " ", "A" To "Z", "a" To "z" 
  putbits 0, c ' 第一組中的字符 
  Case "!
                         " To "/", ":" To "@", "[" To "`", "{" To "~", Chr(1) To Chr(31) 
  putbits 1, c ' 第二組中的字符 
  Case Else 
  putbits 2, c ' 其它字符 
  End Select 
  Next i 
  putbits 3, Chr(0) 
  End If 
  DoCompress = comped 
  End Function 
  Sub putbits (flag%, cc$) ' 壓縮冗余的比特位(bits) 
  Dim i%, c As String * 1 
  c = cc 
  Select Case flag 
  Case 0 '對第一組中的字符內(nèi)碼進行重新定位 
  Select Case c 
  Case " " 
  c = Chr(1) 
  Case "0" To "9" 
  c = Chr(Asc(c) - 46) 
  Case "A" To "Z" 
  c = Chr(Asc(c) - 53) 
  Case "a" To "z" 
  c = Chr(Asc(c) - 59) 
  End Select 
  Case 1 '對第二組中的字符內(nèi)碼進行重新定位 
  Select Case c 
  Case "!" To "/" 
  c = Chr(Asc(c) - 32) 
  Case ":" To "@" 
  c = Chr(Asc(c) - 42) 
  Case "[" To "`" 
  c = Chr(Asc(c) - 68) 
  Case "{" To "~" 
  c = Chr(Asc(c) - 94) 
  Case Chr(1) To Chr(31) 
  c = Chr(Asc(c) + 32) 
  End Select 
  For i = 1 To 6 
  putbit 0 
  Next i 
  Case 2 
  For i = 1 To 12 
  putbit 0 
  Next i 
  For i = 1 To 8 
  If (Asc(c) And ebitmask(i)) <> 0 Then 
  putbit 1 
  Else 
  putbit 0 
  End If 
  Next i 
  Case 3 
  For i = comping_p + 1 To 9 
  putbit 0 
  Next i 
  End Select 
  If flag < 2 Then 
  For i = 1 To 6 
  If (Asc(c) And ebitmask(i + 2)) <> 0 Then 
  putbit 1 
  Else 
  putbit 0 
  End If 
  Next i 
  End If 
  End Sub 
  Sub putbit (bit%) ' 設置比特位(bit) 
  comping_p = comping_p + 1 
  If comping_p > 8 Then 
  comped = comped + comping 
  comping = Chr(0) 
  comping_p = 1 
  End If 
  If bit = 1 Then 
  comping = Chr(Asc(comping) Or ebitmask(comping_p)) 
  End If 
  End Sub 
  Function UnDoCompress (in_buf$) As String ' 對輸入的字符串進行解壓縮 
  Dim bits_buf%, out_buf$, c As String * 1, comped_len& 
  comped = in_buf: bit_p = 1: comped_len = Len(comped) * 8 
  Do While bit_p <= comped_len 
  If comped_len - bit_p < 5 Then 
  Exit Do 
  End If 
  bits_buf = getbits(6) 
  If bits_buf <> 0 Then ' 根據(jù)控制字符判定字符的組別 
  Select Case bits_buf 
  Case 1 
  c = " " 
  Case 2 To 11 ' "0" To "9" 
  c = Chr(bits_buf + 46) 
  Case 12 To 37 ' "A" To "Z" 
  c = Chr(bits_buf + 53) 
  Case 38 To 63 ' "a" To "z" 
  c = Chr(bits_buf + 59) 
  End Select 
  out_buf = out_buf + c 
  Else 
  If bit_p > comped_len Then 
  Exit Do 
  End If 
  bits_buf = getbits(6) 
  If bits_buf <> 0 Then 
  Select Case bits_buf 
  Case 1 To 15 ' "!" To "/" 
  c = Chr(bits_buf + 32) 
  Case 16 To 22 ' ":" To "@" 
  c = Chr(bits_buf + 42) 
  Case 23 To 28 ' "[" To "`" 
  c = Chr(bits_buf + 68) 
  Case 29 To 32 ' "{" To "~" 
  c = Chr(bits_buf + 94) 
  Case 33 To 63 ' Chr(1) To Chr(31) 
  c = Chr(bits_buf - 32) 
  End Select 
  out_buf = out_buf + c 
  Else 
  bits_buf = getbits(8) 
  out_buf = out_buf + Chr(bits_buf) 
  End If 
  End If 
  Loop 
  UnDoCompress = out_buf 
  End Function 
  Function getbits (numb As Integer) As Integer ' 獲取比特位(bit) 
  Dim byte_p&, bit_o%, byte1 As String * 1, i%, j%, k%, m% 
  byte_p = (bit_p / 8) + 1: bit_o = bit_p Mod 8 
  byte1 = Mid(comped, byte_p, 1): j = 0: k = 0 
  For i = bit_o To 8 
  k = k + 1 
  If k > numb Then 
  Exit For 
  End If 
  If (Asc(byte1) And ebitmask(i)) <
                         > 0 Then 
  j = j Or e