英辞郎の辞書ファイル(テキスト形式)をPDIC 1行テキスト形式に変換する

英辞郎のテキスト形式の辞書ファイルが入手できなくなったと聞いて、あわてて『英辞郎 第八版(辞書データVer.141/2014年8月8日版)』を入手した。付属する辞書はVer.1.41。以前購入したのがVer.1.02(2007年03月ごろ)なので、8年半ぶりだ。

私の英辞郎の使い道は2つ。1つはブラウザのマウスオーバー辞書として使う。今までFirefoxのMouseoverDictionaryというアドオンを使っていたのだが、メンテナンスが止まって久しい上に、いつの間にか辞書ファイルを読み込めなくなっていた。そんなわけで、Chromeに乗り換えてChrodicを使おうか検討している。

もう1つは、コンソールで動く自作の辞書引きソフトの辞書データとしての利用。こちらは英辞郎の辞書ファイルではなく、PDICの1行テキスト形式のデータを使う。高速にデータを検索するために、辞書ファイルを二分探索しているので、見出語(データ先頭の英単語のフィールド)がASCIIの昇順にソートされている必要がある。

データ形式PDICで変換できるはずだが、修行を兼ねて自前で変換してみることにした。入力(英辞郎の辞書ファイル)も出力(PDIC 1行テキスト形式に変換したもの)もCP932であるため、多言語の扱いに長けている印象のあるRubyで実装してみた。

#!/usr/bin/env ruby

# edic2pdic1.rb
# 英辞郎の辞書ファイル(テキスト形式)を
# PDICの 1行テキスト形式に変換する。
# PDICによる変換との差異:
# * ルビ(読み仮名)を全て削除する(PDICでは一部削除されない)
# * 【発音】の項目を削除しない。
# * 【レベル】の項目を削除しない。
# * 出力結果はソートされていない(元ファイルでの定義順に準じる)
# * 改行コードが環境依存。

Encoding.default_external = 'cp932'
Encoding.default_internal = 'cp932'

def print_prev(prev, key)
  printf "%s /// %s\n", key, prev[key]
end

prev = {}
keys = []

ARGF.each do |line|
  line.chomp!
  line.sub! /^/, ''
  line.gsub! //, ' \ '        # 訳語部、用例部の区切りを置換
  line.gsub! /[^]+/, ''   # 読み仮名を削除

  if line =~ /^([^{]+)  {([^}]+)} : (.+)$/
    words = $1
    body = sprintf("【%s】%s", $2, $3)
  elsif line =~ /^([^:]+) : (.+)$/
    words = $1
    body = $2
  else
    line =~ /^(.+) : (.+)$/
    words = $1
    body = $2
  end

  words.split(' ; ').each do |word|
    if prev.has_key?(word)
      # 見出語が同じなら、連結して 1行にする
      prev[word] << ' \ ' << body
    else
      # 同じ見出語のデータが離れて記録されていることがある。
      # データを何件か溜めておき、見出語が既出かチェックする。
      # (溜める件数の上限よりも離れていたら、諦める。
      #   さすがに全データをオンメモリに保持するのは厳しい)
      keys.push word
      if keys.size > 100
        key = keys.shift
        print_prev prev, key
        prev.delete key
      end
      prev[word] = body
    end
  end
end

keys.each {|key| print_prev prev, key }

(元のスクリプトには「# encoding: cp932」を記述しているが、文字化け防止のため、上記コードからは取り除いている)

スクリプト中のコメントにも書いたが、このコードでは変換元の辞書ファイルのデータ順に応じた順番で出力する。英辞郎の辞書ファイルは見出語が英大文字・小文字の区別なくアルファベット順にソートされているため、得られる出力もそうなる。

しかし私が欲しいのは見出語がASCII順――英大文字と小文字を区別してソートされた順番のファイルだ。ということで、先ほどのRubyスクリプトをラッピングするシェルスクリプトを作成した。

#!/bin/sh

# edic2pdic1.sh
# 英辞郎の辞書ファイル(テキスト形式)を
# PDICの 1行テキスト形式に変換する。
# ついでに見出語をキーとして ASCIIの昇順にソートし、
# 改行をCRLFにする。

set -u
umask 0022
IFS=`printf ' \t\n_'`; IFS=${IFS%_}
export IFS LC_ALL=C LANG=C

ruby `dirname \`realpath "$0"\``/edic2pdic1.rb ${@+"$@"}    |
sed 's!///!|!'                                              |
sort -t '|' -k 1,1                                          |
sed 's!|!///!'                                              |
tr -d \\r                                                   |
sed 's/$/\r/'

このシェルスクリプトは、先ほどのRubyスクリプト(edic2pdic1.rb)と同じディレクトリに置いて実行するという想定で作成している。