-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdict_hack.txt
193 lines (136 loc) · 8.4 KB
/
dict_hack.txt
1
2
3
4
5
6
7
8
9
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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
21世紀字典5.0版檔案格式說明:
---------------------------------------------------------------------------
[a-z].i50 檔案格式剖析:
這是字典的索引檔。block格式為:
┌┬┬┬┐
│││││
└┴┴┴┘
每個方格代表一個byte。(以下皆同)
一個索引檔是由很多block所組成, 每個block長度固定4 bytes, 其實也就是
以little-endian格式儲存的整數(在本文件中「整數」一律是4 bytes儲存)。
這個整數稱為level 3 index。
---------------------------------------------------------------------------
[a-z].d50 檔案格式剖析:
這是字典的字庫檔。block可分三種格式:
┌┐
││ (第一種格式)
└┘
┌┬┬────── ───┐
│││ ..... │ (第二種格式)
└┴┴────── ───┘
┌┬─┬───── ───┐
││ │ ..... │ (第三種格式)
└┴─┴───── ───┘
Ⅰ Ⅱ Ⅲ
一個字庫檔是由很多block所組成, 每個block長度不一。有的block只有Ⅰ
部份, 有的還有Ⅱ、Ⅲ部份, 而Ⅱ部份的長度又可以是1或2 bytes。
判斷是否為第一種格式的方法:
若Ⅰ部份這個byte是 0x6? 或是 0x5?, 則就是第一種格式。
判斷是否為第二、三種格式的方法:
若非第一種格式, 則看Ⅱ部份的第一個byte是否為0xFF, 是的話則為第三
種格式, 也就是Ⅱ部份是2 bytes。否則為第二種格式, 也就是Ⅱ部份是1
byte。
每個部份所代表意義說明:
Ⅰ部份: 這個block儲存的資料種類:
0x1? 為 單字本身及使用頻率
例: 0x11 為 次常用字
0x2? 為 單字音標
0x3? 為 片語解釋
0x5? 為 <<同義字>>
0x6? 為 單字詞性
例: 0x61 為 名詞
0x8? 為 單字解釋
0x9? 為 例句
0xA? 為 例句
0xC? 為 額外資訊
0xD? 為 例句
0xE? 為 例句
以上只是大概的分類, 還有一些細節請見程式碼的
printLine()函式。另外排版上的資訊(如縮格)是存在後
面的4 bits中, 如 0x9E 是例句, 且縮格2個字元。
Ⅱ部份: 資料長度。若為第三種格式者, 則資料長度為兩byte值之和。
Ⅲ部份: 實際資料。長度由Ⅱ部份之值決定, 注意, 這資料不是null-
terminated string。實際資料有編碼過, 每個byte都必需再
與 0xA5 做XOR運算得到的才是真正資料。
---------------------------------------------------------------------------
如何做英漢查詢:
每個英文單字的資料都是由一個以上的block構成, 第一個block一定是前面所
述的第二種格式, 且Ⅰ部份一定是0x1?, 若不是那就是資料檔案有問題。
以 "dictionary" 為例,
1.讀取Index.gdbm。用 "dictionary" 當key, 讀得一值, 稱level 2 index。
2.讀取d.i50。level 2 index本身先乘以4, 然後用乘以4後的值將檔案指標
移動(用lseek() system call), 讀出level 3 index。並且再往下多讀一個
值, 此值與level 3 index之差為資料長度data_len。
level 3 index雖然是整數型態, 但只有後面的21 bits是有用的, 前面的11
bits我一直沒搞懂有何用意。故level 3 index必需與 0x1FFFFF 做AND運算
所得才是真正的level 3 index。
3.讀取d.d50。在level 3 index所指的地方(lseek()), 讀出data_len長度的資
料, 此即為 "dictionary" 的資料。
4.用前述『[a-z].d50 檔案格式剖析』一一印出資料內容。
---------------------------------------------------------------------------
ce.i50 檔案格式剖析:
這是漢英查詢的level 0索引檔。block格式為:
┌┬┬┬┐
│││││
└┴┴┴┘
一個漢英查詢level 0索引檔是由很多block所組成, 每個block為固定4 bytes,
也就是以little-endian格式儲存的整數。這個整數稱為level 0 index。
---------------------------------------------------------------------------
ce.d50 檔案格式剖析:
這是漢英查詢的level 1索引檔。block格式為:
┌┬┬─ ─┬┬┬┐
│││ ... ││││
└┴┴─ ─┴┴┴┘
└──┘
ⅠⅡ Ⅲ Ⅳ
一個漢英查詢的level 1索引檔是由很多block所組成, 每個block長度不一。
不過都一定有Ⅰ、Ⅱ、Ⅲ、Ⅳ四部份。其中Ⅳ部份可以重複很多個。
每個部份所代表意義說明:
Ⅰ部份: 中文詞彙的長度。每個中文字都有2 bytes, 故此值必為偶數。
Ⅱ部份: 共有幾個英文單字含有此中文詞彙。
Ⅲ部份: 中文詞彙(請見『如何做漢英查詢』)。長度由Ⅰ部份之值決定。
長度可以是0。
Ⅳ部份: level 2 index。第1個byte是英文單字的字首大寫。第2、3 byte
是以 little-endian 儲存的「短整數」(short int, 長度為2
bytes)。這個短整數就是level 2 index。由於一個中文詞彙可能對
應到多個英文單字, 所以Ⅳ部份重複出現的數目是由Ⅱ部份之值所
決定。
---------------------------------------------------------------------------
如何做漢英查詢:
漢英查詢較英漢查詢麻煩一點, 一共要經過四層的index才能查到。
以「字典」為例,
1.算出「字」在整個Big-5碼中的offset, 這稱為level 0 index。
Big-5碼定義了13053個中文字(常用字與次常用字), 例如「一」
的內碼是A440, 但在整個Big-5碼表中「一」是排第一個, 故0
是「一」的offset, 同理「乙」的內碼是A441, 則「乙」的offset
就是1, 以此類推。詳細計算法請參照程式碼的Big5toOffset()函式。
2.讀取ce.i50。level 0 index本身先乘以4, 然後用乘4後之值所指的
地方, 讀出level 1 index。並且再往下多讀一個值, 此值與level 1
index之差為資料長度buflen。
3.讀取ce.d50。在level 1 index所指的地方, 讀出長度為buflen
的資料。這些資料就包含所有以「字」為首的中文詞彙, 從最開
頭的「字」「字、詞變義」「字之形成的」... 一直到最後的
「字體轉換鍵」。前述『ce.d50 檔案格式剖析』中, Ⅲ部份要
特別注意的是, 它不包含中文詞彙的第一個中文字, 也就是說
真正去依資料結構讀時, Ⅲ部份讀到的是「」「、詞變義」
「之形成的」... 「體轉換鍵」。
4.接著用線性搜尋在所有block中, 找到Ⅲ部份符合「典」的block。之
後可得英文單字的字首大寫與level 2 index。此例中兩個英文單字的
字首大寫分別為D與W。(這兩個英文單字會是dictionary與wordbook)
5.讀取 d.i50 與 w.i50。此時已有level 2 index, 請參照前面
『如何做英漢查詢』的步驟2. 將dictionary與wordbook兩字的資
料讀出。
---------------------------------------------------------------------------
如何建立Index.gdbm:
gdbm (GNU dbm)是一套簡單的資料庫函式, 它是用hash table來實作的。
用法就是給定一個key, 可以快速地儲存、查詢、刪除該key所對應的
content。
建立Index.gdbm的方法跟『如何做英漢查詢』的步驟差不多:
1.將 a.i50 檔案整個讀入, 依照 a.i50 的每個level 3 index去讀 a.d50
的內容。
2.每讀一個level 3 index, 就讀入相對的 a.d50 的資料, 只取 單字 的資料
(見『[a-z].d50 檔案格式剖析』)。 把 單字 當key, level 2 index當content
存入Index.gdbm。level 2 index之值就是一個計數器的值, 從0開始, 每讀
一個level 3 index就增加1。
3.如有一字多義的情況, 則把level 2 index附加在原有的level 2 index之後。
4.重複 步驟1. 這次改為 b.i50 與 b.d50。以此類推。