Hatena::Grouprubyist

Rubyで遊ぶよ

 | 

2009-05-03

英辞郎の辞書ファイルをUTF-8に変換してsqliteでインデックスしてみる

12:43

自分が購入した英辞郎は109版で、ファイル名は EIJI-109.TXT となっていた。

辞書ファイルは、Shift JIS 形式で一行に一単語 (および熟語) と、その説明が平文で入っている。


Shift JIS に無い文字は UTF-8 となっている。例えば発音記号の一部など。

この場合、

open('EIJI-109.TXT','r:SHIFT_JIS:UTF-8') {|f|
  while line = f.gets

などとやってしまうと、元々 UTF-8 な文字のところで「エンコーディング変換できないよ」というエラーが出ることになる。

./create.rb:12:in `convert': "\x87\x92" from Shift_JIS to UTF-8 (Encoding::UndefinedConversionError)

ちなみに "\x87\x92" は発音記号の "∫" (縦長S) にあたるらしい。

% ruby19 -rkconv -e 'puts "\x87\x92".toutf8'

Ruby 1.9 からの

Encoding::Converter.new('SHIFT_JIS','UTF-8')

を使ってもいいのだけど、これだと上と同じエラー (Encoding::UndefinedConversionError) が出ることになる。


せっかくなので速度も比較してみた。

Encoding::Converter と kconv の組み合わせ。

#!/usr/local/ruby1.9/bin/ruby
# -*- coding: utf-8 -*-
require 'kconv'

ec = Encoding::Converter.new('SHIFT_JIS','UTF-8')
lines = []
File.open('EIJI-109.TXT','r:SHIFT_JIS') { |f|
  while l = f.gets
    begin
      u = ec.convert(l.chomp)
    rescue
      u = l.chomp.toutf8
    end
    lines.push(u)
  end
}

kconv だけ。

#!/usr/local/ruby1.9/bin/ruby
# -*- coding: utf-8 -*-
require 'kconv'

lines = []
File.open('EIJI-109.TXT','r:SHIFT_JIS') { |f|
  while l = f.gets
    lines.push(l.chomp.toutf8)
  end
}

変換にかかった時間は、Encoding::Converter と kconv の組み合わせが19秒ぐらいだったのに対し、kconv だけのものが33秒ぐらいだった。

このぐらいだったら kconv だけでやってしまおう。(どうせ sqlite の処理のほうがはるかに時間がかかるんだし)


とりあえずエスケープ処理等のとことまったく無視してデータベースを作ると、こうなる。(下準備として sudo gem install sqlite3-ruby が必要。)

#!/usr/local/ruby1.9/bin/ruby
# -*- coding: utf-8 -*-
require 'kconv'
require 'sqlite3'

lines = []
File.open('EIJI-109.TXT','r:SHIFT_JIS') { |f|
  while l = f.gets
    lines.push(l.chomp.toutf8)
  end
}

p lines.length

db = SQLite3::Database.new('eiji.db')
db.execute("CREATE TABLE dictionary (line COLLATE NOCASE);")

db.transaction do
  sql = "INSERT INTO dictionary VALUES (?)"
  lines.each { |line|
    db.execute(sql, line)
  }
end

db.execute("CREATE INDEX dictionary_idx ON dictionary (line);")
db.close

ポイントは、この2行。

db.execute("CREATE TABLE dictionary (line COLLATE NOCASE);")
db.execute("CREATE INDEX dictionary_idx ON dictionary (line);")

LIKE 演算子で前方一致検索をする場合、これをやるかやらないかで速度がまったく変わってくる。詳しくは以下より。

インデックス処理にかかる時間は35秒ぐらいで、テーブル作成にかかる時間 (100秒ぐらい) に比べたら大したことないぐらいか。


これでとりあえずは検索できる。検索時間は一瞬。

% sqlite3 -line eiji.db 'SELECT line FROM dictionary WHERE line LIKE "■z%" LIMIT 20;'
 line = ■z  {名-1} : Z 字形{じけい}(のもの)
 line = ■Z  {名-1} : アルファベットのジー、ゼット〔大文字〕
 line = ■z  {名-2} : 《数学》第3未知数{みちすう}
 line = ■Z  {名-2} : 未知のもの、最後のもの
 line = ■Z  {名-3} : 《ギリシャ文字》ツェータ、ゼータ、Zeta◆小文字は、ζ◆【参考】Greek alphabet(ギリシャ文字)
 line = ■Z as in zebra : ゼブラの Z◆電話などでアルファベットの確認を行うときに使われる言い方。これ以外の単語が使われることもある。Zulu も使用可。■・"Did you say Z?"  "Yes. It's Z. Z as in Zebra." 「Z って言った?」「はい、Z です。ゼブラの Z」
 line = ■Z buffer : Z バッファー
 line = ■Z Camelopardalis star : きりん座 Z 型星
 line = ■Z chromosome : Z 染色体{せんしょくたい}
 line = ■z distribution : z 分布{ぶんぷ}
 line = ■Z filament : Z フィラメント
 line = ■Z fold : 《印刷》Z 折り◆経本折り、または6頁巻き折り◆【同】accordion fold
 line = ■Z for Zebra : ズィーブラの Z◆電話でアルファベットを正確に伝えるときに使う
 line = ■Z gene : Z 遺伝子{いでんし}
 line = ■Z line : Z 線
 line = ■Z therapy : Z 療法{りょうほう}
 line = ■z transformation : z 変換{へんかん}
 line = ■Z twist : Z より、右より
 line = ■Z twist yarn : Z より糸
 line = ■Z's  {名-1} : グーグー、ガーガー、ズーズー◆いびきを示す擬音語。漫画によく見られる。

インデックス処理をしかなったら、テーブルの最後のほうまで検索するクエリだとだいぶ遅くなる。(1秒 ぐらい)


何も考えずにやったけど、テーブルに登録する時にエスケープする必要はあるのだろうか? 明日はそのへんを考える。


続き。

ゲスト



トラックバック - http://rubyist.g.hatena.ne.jp/edvakf/20090503
 |