StarDictの辞書アプリ
StarDictの辞書ファイル形式で調べたファイル形式に基づいて、簡単なコンソール辞書アプリを作ってみた。
実行結果はこんな感じ。なんか変な辞書だな、これ。
search: あい あい => [アイ~あ行~]ใอ ไอ / คราม / ความชอบพอ, ความรัก, พิสมัย, ศฤงคาร, เสนหา, เสน่หา search: あいし あいしあう あいしあえないでしょう? あいしいゆうちりょうしつ あいしているのにくちにだしていえない あいしてる あいしてるっていわなくてもいいわ あいしてるとくりかえしていう あいしてるよ、ちゅっちゅっ あいしゃどう あいしゃどうをぬる あいしょう あいしょうがいい あいしょうがわるい あいしんびいなす search: あいして あいしているのにくちにだしていえない => [愛しているのに口に出していえない。]ไม่กล้าพูดว่าผมรักคุณทั้งๆ ที่รัก あいしてる => [愛してる]มีใจ あいしてるっていわなくてもいいわ => [愛してるって言わなくてもいいわ。]ไม่ต้องมาพูดว่ารัก あいしてるとくりかえしていう => [愛してると繰り返して言う]ย้ำถึงคำว่ารัก あいしてるよ、ちゅっちゅっ => [愛してるよ、チュッチュッ。]รักกันจุ๊บ ๆ
ソースはこう。
工夫したのはインデックスのインデックスを最初に持っておくところくらいか。
インデックスの段階を増やせば、メモリの少ない環境でも動くようにできるはず。
def dictionary = new Dictionary() def input = new BufferedReader(new InputStreamReader(System.in)) while (true) { print "search: " key = input.readLine() def resultList = dictionary.search(key) for (idx in resultList) { println idx.word if (resultList.size() <= 10) { println "=> " + dictionary.getDefinition(idx) } } println "" } class Dictionary { def indexSize = 3 def indexes def Dictionary() { indexes = loadIndex(indexSize) } def search(originalKey) { def key = originalKey if (originalKey.length() > indexSize) key = originalKey.substring(0, indexSize) def start = searchStart(indexes, key, 0, indexes.size()-1) def end = searchEnd(indexes, key, 0, indexes.size()-1) def length = 0 def count = 0 for (idx in indexes.subList(start, end+1)) { length += idx.indexLength count += idx.count } def completeIndexes = loadIndex(-1, indexes.get(start).indexOffset, length) if (completeIndexes.size() > 0 && completeIndexes.get(0).word == originalKey) return [completeIndexes.get(0)] return completeIndexes.subList( searchStart(completeIndexes, originalKey, 0, completeIndexes.size()-1), searchEnd(completeIndexes, originalKey, 0, completeIndexes.size()-1)+1) } def searchStart(list, key, start, end) { if (start == end || end < 0) return start def i = (int)Math.floor((start+end)/2) /* println "start:${start}, end:${end}, i:${i}"*/ def wd = list.get(i).word if (key.length() < wd.length()) wd = wd.substring(0, key.length()) def c = wd.compareTo(key) /* println "start:${start}, end:${end}, i:${i}, wd:${wd}, key:${key}, c:${c}, wd:${wd.getBytes()}, key:${key.getBytes()}"*/ if (c >= 0) { return searchStart(list, key, start, i) } else { if ((end-start)==1) i = end return searchStart(list, key, i, end) } } def searchEnd(list, key, start, end) { // 見つからない場合はsearchStartの結果-1が返る。 if (start == end || end < 0) return end def i = (int)Math.ceil((start+end)/2) if (i>end) i = end def wd = list.get(i).word if (key.length() < wd.length()) wd = wd.substring(0, key.length()) def c = wd.compareTo(key) /* println "start:${start}, end:${end}, i:${i}, wd:${wd}, key:${key}, c:${c}"*/ if (c <= 0) { return searchEnd(list, key, i, end) } else { if ((end-start)==1) i = start return searchEnd(list, key, start, i) } } def parseInt(byte b1, byte b2, byte b3, byte b4) { return (b1 & 0xff) * 256*256*256 + (b2 & 0xff) * 256*256 + (b3 & 0xff) * 256 + (b4 & 0xff) } def loadIndex(indexSize = 3, indexOffset = 0, indexLength = -1) { /* println "read ${indexOffset} - ${indexOffset + indexLength}"*/ if (indexOffset < 0) indexOffset = 0 def indexFile = new File("test.idx") if (indexLength == -1) indexLength = indexFile.length() def buf = new byte[indexLength] def fileInputStream = new BufferedInputStream(new FileInputStream(indexFile)) fileInputStream.skip(indexOffset) fileInputStream.read(buf) fileInputStream.close() def i=0 int offset = 0 def indexes = new ArrayList(); def wd = "" def prev = "" while (i<buf.length) { if (buf[i] == 0) { wd = new String(buf, offset, i-offset, "UTF-8") if (indexSize > 0) { wd = wd.substring(0, indexSize>wd.length() ? wd.length() : indexSize) } if (prev != wd) { indexes.add(new Index( word:wd , offset:parseInt(buf[i+1], buf[i+2], buf[i+3], buf[i+4]) , length:parseInt(buf[i+5], buf[i+6], buf[i+7], buf[i+8]) , indexOffset:offset , indexLength:i-offset+9)) } else { def index = indexes.get(indexes.size()-1) index.addCount() index.addIndexLength(i-offset+9) } prev = wd offset = i+9 i = i+8 } i++ } return indexes } def getDefinition(index) { def defIn = new FileInputStream("test.dict") def buf = new byte[index.length] defIn.skip(index.offset) defIn.read(buf) defIn.close() return new String(buf, "UTF-8") } } class Index { def word def offset def length def indexOffset def indexLength def count = 1 def addCount() {count++} def addIndexLength(l) {indexLength += l} String toString() { return "[word: ${word},\toffset:${offset},\tlength:${length},\tindexOffset:${indexOffset},\tindexLength:${indexLength}\tcount:${count}]" } }