Jeswang's Blog

盲目跟随还是独立去做,To be or not to be?

「鼠须管」导入搜狗 Bin 词库

| Comments

「鼠须管」真是个好用的输入法。最大的优势就是反应很快,快到换用「搜狗输入法」就会有输入明显变卡的感觉。但是因为在搜狗的用户词库里有了一些自己输入过的词,所以每次想要换用「鼠须管」,总是使用时间不长就换回了搜狗。

没办法,只能自己想办法把用户词库加到「鼠须管」里了。

准备阶段

问:搜狗输入法的用户词库在哪里?

答:输入一个会被记录的词,这个文件就会更新:

1
/Users/用户名/Library/Input Methods/Sogou/SogouPY.users/00000001/sgim_usr_v1.bin

一搜文件名,果然是它。

问:这可是个二进制文件啊,大哥。要怎么解析它?

答:使用 Hex Field 看一下,确实头大:

但是搜了一下发现居然有人解析过,就是这个开源项目 studyzy/imewlconverter (真佩服他二进制解读的能力)。唯一比较遗憾的是这是一个 Windows 下的程序,Mac 下没法直接使用。

这下开源的好处就体现出了来,自己动手丰衣足食吧。

导出搜狗词库

解析的工作按照 imewlconverter 中的 SougouPinyinBin.cs 来就可以了。

二进制的文件是大端还是小端

Hex Field 查看发现小端的输出比较合理,所以认定是小端。在读取的时候,需要使用 Python 的 Struct 包读取时指明是小端:

1
2
3
4
def read_int_32(f):
      byte = f.read(4)
      byte = struct.unpack("<L", byte)
      return byte[0]

二进制中的文字使用的是什么编码?

我用户词库的第一个字是“厑”,使用 UnicodeChecker 就能看到使用的应该是Unicode Hex 或者 UTF-16 Hex。

使用 Codes 模块就可以了:

1
word = codecs.utf_16_le_decode(f.read(word_count))[0]

导出词库的格式

导入词库的时候,要将信息保存在 .dic 的文件里,.dic 文件的格式为:

1
中文词组[Tab]以空格分割的拼音组[Tab]频率

具体的代码

导出搜狗用户词库 (read_sogou_bin.py) download
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
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
# -*- coding: UTF-8 -*-
import sys
import struct
import codecs

PinYinDic = [
"a",
"ai",
"an",
"ang",
"ao",
"ba",
"bai",
"ban",
"bang",
"bao",
"bei",
"ben",
"beng",
"bi",
"bian",
"biao",
"bie",
"bin",
"bing",
"bo",
"bu",
"ca",
"cai",
"can",
"cang",
"cao",
"ce",
"cen",
"ceng",
"cha",
"chai",
"chan",
"chang",
"chao",
"che",
"chen",
"cheng",
"chi",
"chong",
"chou",
"chu",
"chua",
"chuai",
"chuan",
"chuang",
"chui",
"chun",
"chuo",
"ci",
"cong",
"cou",
"cu",
"cuan",
"cui",
"cun",
"cuo",
"da",
"dai",
"dan",
"dang",
"dao",
"de",
"dei",
"den",
"deng",
"di",
"dia",
"dian",
"diao",
"die",
"ding",
"diu",
"dong",
"dou",
"du",
"duan",
"dui",
"dun",
"duo",
"e",
"ei",
"en",
"eng",
"er",
"fa",
"fan",
"fang",
"fei",
"fen",
"feng",
"fiao",
"fo",
"fou",
"fu",
"ga",
"gai",
"gan",
"gang",
"gao",
"ge",
"gei",
"gen",
"geng",
"gong",
"gou",
"gu",
"gua",
"guai",
"guan",
"guang",
"gui",
"gun",
"guo",
"ha",
"hai",
"han",
"hang",
"hao",
"he",
"hei",
"hen",
"heng",
"hong",
"hou",
"hu",
"hua",
"huai",
"huan",
"huang",
"hui",
"hun",
"huo",
"ji",
"jia",
"jian",
"jiang",
"jiao",
"jie",
"jin",
"jing",
"jiong",
"jiu",
"ju",
"juan",
"jue",
"jun",
"ka",
"kai",
"kan",
"kang",
"kao",
"ke",
"kei",
"ken",
"keng",
"kong",
"kou",
"ku",
"kua",
"kuai",
"kuan",
"kuang",
"kui",
"kun",
"kuo",
"la",
"lai",
"lan",
"lang",
"lao",
"le",
"lei",
"leng",
"li",
"lia",
"lian",
"liang",
"liao",
"lie",
"lin",
"ling",
"liu",
"lo",
"long",
"lou",
"lu",
"luan",
"lue",
"lun",
"luo",
"lv",
"ma",
"mai",
"man",
"mang",
"mao",
"me",
"mei",
"men",
"meng",
"mi",
"mian",
"miao",
"mie",
"min",
"ming",
"miu",
"mo",
"mou",
"mu",
"na",
"nai",
"nan",
"nang",
"nao",
"ne",
"nei",
"nen",
"neng",
"ni",
"nian",
"niang",
"niao",
"nie",
"nin",
"ning",
"niu",
"nong",
"nou",
"nu",
"nun",
"nuan",
"nue",
"nuo",
"nv",
"o",
"ou",
"pa",
"pai",
"pan",
"pang",
"pao",
"pei",
"pen",
"peng",
"pi",
"pian",
"piao",
"pie",
"pin",
"ping",
"po",
"pou",
"pu",
"qi",
"qia",
"qian",
"qiang",
"qiao",
"qie",
"qin",
"qing",
"qiong",
"qiu",
"qu",
"quan",
"que",
"qun",
"ran",
"rang",
"rao",
"re",
"ren",
"reng",
"ri",
"rong",
"rou",
"ru",
"rua",
"ruan",
"rui",
"run",
"ruo",
"sa",
"sai",
"san",
"sang",
"sao",
"se",
"sen",
"seng",
"sha",
"shai",
"shan",
"shang",
"shao",
"she",
"shei",
"shen",
"sheng",
"shi",
"shou",
"shu",
"shua",
"shuai",
"shuan",
"shuang",
"shui",
"shun",
"shuo",
"si",
"song",
"sou",
"su",
"suan",
"sui",
"sun",
"suo",
"ta",
"tai",
"tan",
"tang",
"tao",
"te",
"tei",
"teng",
"ti",
"tian",
"tiao",
"tie",
"ting",
"tong",
"tou",
"tu",
"tuan",
"tui",
"tun",
"tuo",
"wa",
"wai",
"wan",
"wang",
"wei",
"wen",
"weng",
"wo",
"wu",
"xi",
"xia",
"xian",
"xiang",
"xiao",
"xie",
"xin",
"xing",
"xiong",
"xiu",
"xu",
"xuan",
"xue",
"xun",
"ya",
"yan",
"yang",
"yao",
"ye",
"yi",
"yin",
"ying",
"yo",
"yong",
"you",
"yu",
"yuan",
"yue",
"yun",
"za",
"zai",
"zan",
"zang",
"zao",
"ze",
"zei",
"zen",
"zeng",
"zha",
"zhai",
"zhan",
"zhang",
"zhao",
"zhe",
"zhei",
"zhen",
"zheng",
"zhi",
"zhong",
"zhou",
"zhu",
"zhua",
"zhuai",
"zhuan",
"zhuang",
"zhui",
"zhun",
"zhuo",
"zi",
"zong",
"zou",
"zu",
"zuan",
"zui",
"zun",
"zuo",
"A",
"B",
"C",
"D",
"E",
"F",
"G",
"H",
"I",
"J",
"K",
"L",
"M",
"N",
"O",
"P",
"Q",
"R",
"S",
"T",
"U",
"V",
"W",
"X",
"Y",
"Z"
]

def read_int_16(f):
      byte = f.read(2)
      byte = struct.unpack("<h", byte)
      return byte[0]


def read_int_32(f):
      byte = f.read(4)
      byte = struct.unpack("<L", byte)
      return byte[0]


def read_bin_file(path):
  with open(path, "rb") as f:
          f.seek(0x18, 0)
          total_word_count = read_int_32(f)
          print "Total word is %s" % total_word_count
          current_word_num = 0
          f.seek(0x30, 0)

          res = []
          while current_word_num < total_word_count :
              same_py_count = read_int_16(f)
              unknow_var = read_int_16(f)
              py_count = read_int_16(f) / 2

              py_str = []

              for i in range(py_count):
                  idx = read_int_16(f)
                  if idx < len(PinYinDic) and idx >= 0:
                      py_str.append(PinYinDic[idx])
                  else:
                      py_str.append("--")

              for i in range(same_py_count):
                  word_count = read_int_16(f)
                  word = codecs.utf_16_le_decode(f.read(word_count))[0]
                  count = read_int_16(f)
                  count2 = read_int_16(f)
                  unknow_var2 = read_int_32(f)
                  word_dic = {"count":count, "word": word, "pinyin": py_str}
                  res.append(word_dic)
                  print "current_word_num: %s and pinyin %s" % (current_word_num, py_str)
                  current_word_num += 1
          return res

def try_pinyin():
  print 'Number of arguments:', len(sys.argv), 'arguments.'
  print 'Argument List:', str(sys.argv)
  if len(sys.argv) == 2:
      print PinYinDic[int(sys.argv[1])]

def save_words_to_path(filename, words):
  f = codecs.open(filename, "w", "utf-8")
  for word in words:
      f.write(u"%s\t%s\t%d\n" % (word["word"], " ".join(word["pinyin"]), word["count"]))
  print "共写入 %d 词组" % len(words) 
  f.close()


if __name__ == "__main__":
  path = "/Users/用户名/Library/Input Methods/Sogou/SogouPY.users/00000001/sgim_usr_v1.bin"
  res = read_bin_file(path)
  print res[0]
  save_words_to_path("user.dic", res)

导入 Rime 词库

有了 user.dic 文件以后: - 找到 rime_dict_manager。把这个 bash 脚本保存到 ~/Library/Rime/rime_dict_manager:

  • 拷贝转化后的鼠须管词库(user.dic)至\~/Library/Rime目录;
  • 打开终端,先kill:killall Squirrel
  • 单个导入:./rime_dict_manager -i luna_pinyin user.dic

更多链接

- EOF -

Comments