摘要:拆解 DICT dictionary
DICT Protocol這個專案已經run了很久了,很多我們熟悉的網路版字典查詢或是一些PDA上手機字典,大部分都會使用該標準所產生的
字典檔與索引檔,來提供字典的查詢與使用。DICT Protocol中,幾個重點的檔案格式大致如下:
*.dict:這個是dict主要的字典資源檔,說明白一點,就是整個字典的資訊全部都放在這個檔案上。
*.idx:這個是Index檔,主要採用B-Tree的方式存放.dict檔案中所有字的索引,在DICT的使用上,會先將idx檔載入記憶體中,
幫助快速搜尋。.idx中的檔案,其按照一固定格式排放,格式如下:
word_str; // a utf-8 string terminated by '\0'. 以utf-8存放單字名稱,並且用'\0'當結束字元。
word_data_offset; // word data's offset in .dict file. 代表該單字的資料是從那個一位元開始,舉例來說:god的資料占了30bit,
而下一個字good的offset就是30,他要從第31bit開始取得。在word_data_offset
是使用4bytes來存放資料。
word_data_size; //word data's total size in .dict file. 代表該單字的資料大小。word_data_size用4bytes存放資料。
*.ifo:資訊檔,用於紀錄該.dict的版本資訊、字數量與其他相關的參數。
其他相關的資訊,大家可參考「StarDict Dictionary Format」或是「http://code.google.com/p/babiloo/wiki/StarDict_format」。
StarDict這個網站提供了相當多的字庫檔,因此,我找了一個字典來當做拆解dict的練習。大致上拆解的方式,如下之解釋:
1. 讀取 .idx檔。根據 .idx的format拆解出每個字的三個重要參數:word_str、word_data_offset、word_data_size。
2. 取出的offset需要做位移的補足動作,因為在.idx中存放的是16進位之格式,因此需要做位移的調整。
3. 調整完成後,再讀取 .dict檔,並按照三個參數的值,分別移動指向該 .dict的指標進行移動,讀取資料。
4. 讀取出來的資料再轉回到 .txt或.dat的檔案之中。
下方的程式,主要是透過C#來撰寫,讀取檔案的方式是使用 FileStream,配合 BinaryReader,讓所有資料變成Binary的格式,
在做位移的調整上也會比較好做。另外,分別使用 word_str()、word_offset()、word_length()三個函數來取得三個主要的參數,
在餵入word_offset()與word_length()時,分別各取得固定長度4bytes,接著將4bytes的值根據16進位來調整位移度,並回結果,
最後統一交由getvocaublaryinformation()這個function將單字的資料取出,並且寫入.txt檔中。
private void separate()
{
FileStream objFsIdx = new FileStream("21shijishuangxiangcidian-big5.idx", FileMode.Open);
BinaryReader objBrIdx= new BinaryReader(objFsIdx);
FileStream objFsDict = new FileStream("21shijishuangxiangcidian-big5.dict", FileMode.Open);
BinaryReader objBrDict = new BinaryReader(objFsDict);
TextWriter objTW = new StreamWriter("21data.txt");
//basic parameters
int intOffest = 0;
int intLength = 0;
int intPoint = 0;
int[] intArray=new int[100];
while (objBrIdx.BaseStream.Position < objBrIdx.BaseStream.Length)
{
int intNow = objBrIdx.ReadByte();
intArray[intPoint] = intNow;
if (intNow == 00)
{
//Console.WriteLine(word_str(intArray));
intOffest = word_offset(objBrIdx.ReadBytes(4));
intLength=word_length(objBrIdx.ReadBytes(4));
objTW.WriteLine (word_str(intArray,intPoint) +"\\n"+getvocabularyinformation(objBrDict, intOffest, intLength));
intPoint = 0;
break;
//continue;
}
intPoint += 1;
}
objTW.Close();
}
private String word_str(int[] intArray, int intEnd)
{
String word = "";
for (int intX = 0; intX <intEnd; intX++)
{
if (intArray[intX] != 00)
{
word += Convert.ToChar(intArray[intX]);
}
}
return word;
}
private int word_offset(byte[] byteArray)
{
int offset = 0;
String strHex = "";
for (int intX = 0; intX < byteArray.Length; intX++)
{
strHex+=Convert.ToString(byteArray[intX],16).PadLeft(2,'0');
}
offset = Convert.ToInt32(strHex, 16);
return offset;
}
private int word_length(byte[] byteArray)
{
int length = 0;
String strHex = "";
for (int intX = 0; intX < byteArray.Length; intX++)
{
strHex += Convert.ToString(byteArray[intX], 16).PadLeft(2, '0');
}
length = Convert.ToInt32(strHex, 16);
return length;
}
private String getvocabularyinformation(BinaryReader objBR, int intO, int intL)
{
String strX="";
while (objBR.BaseStream.Position < objBR.BaseStream.Length)
{
objBR.BaseStream.Position = intO;
byte[] objBytes=objBR.ReadBytes(intL);
strX= System.Text.Encoding.UTF8.GetString(objBytes);
strX = strX.Replace("\n", "\\n");
//Console.WriteLine(strX);
break;
}
return strX;
}
以上是個人跟學長在比賽撰寫拆解DICT字典的比賽中一些心得,不過學長用java寫的比我還好,果然薑還是老的辣,希望能對大家有所幫助。
[以下是小紀所提供程式修改的部分]
查StartDict格式時, 剛好看到你的Blog, 我想說薑可能是老的辣, 但語言不一定是老的好, C# 不應會比 JAVA 差
我看了你的程式, 覺得可以改的地方很多, 首先 word_str()、word_offset()、word_length() 這三個函數是多餘的
其中offset 及 length 可以用 位移 的方式來計算, 直接轉char輸出 word_str就可省了.
我把 separate() 改了一下, getvocabularyinformation() 也省, 你看看...
其中offset 及 length 可以用 位移 的方式來計算, 直接轉char輸出 word_str就可省了.
我把 separate() 改了一下, getvocabularyinformation() 也省, 你看看...
01
private void separate()
02
{
03
FileStream objFsIdx = new FileStream("Babylon_English_Chinese_T_.idx", FileMode.Open);
04
FileStream objFsDict = new FileStream("Babylon_English_Chinese_T_.dict", FileMode.Open);
05
06
TextWriter objTW = new StreamWriter("dict.txt");
07
08
int intNow;
09
int intOffest = 0;
10
int intLength = 0;
11
byte[] baBuffer = new byte[512];
12
13
int i = 0;
14
15
for (i = 0; i < objFsIdx.Length; i++)
16
{
17
intNow = objFsIdx.ReadByte();
18
if (intNow == 0)
19
{
20
intOffest = (((((objFsIdx.ReadByte() << 8) | objFsIdx.ReadByte()) << 8) | objFsIdx.ReadByte()) << 8) | objFsIdx.ReadByte();
21
intLength = (((((objFsIdx.ReadByte() << 8) | objFsIdx.ReadByte()) << 8) | objFsIdx.ReadByte()) << 8) | objFsIdx.ReadByte();
22
23
objFsDict.Seek(intOffest, SeekOrigin.Begin);
24
objFsDict.Read(baBuffer, 0, intLength);
25
objTW.Write("\\n{0}\r\n", System.Text.Encoding.UTF8.GetString(baBuffer, 0, intLength).Replace("\n", "\\n"));
26
27
i += 8;
28
}
29
else
30
objTW.Write((char)intNow);
31
}
32
objTW.Dispose();
33
objFsIdx.Dispose();
34
objFsDict.Dispose();
35
}

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

References:
DICT Protocol:http://www.dict.org/links.html
DICT.TW 線上字典:http://dict.tw/
Lingoes Translator :http://www.lingoes.cn/zh/translator/font.htm