KND の備忘録

2015-09-22のメモです。

主に関ジン研(関東ジンギス研究会)からのリンクのためと、食べ歩きの際に前回いつ行ったかを自分で検索するために記述を残しています。稀に雑多なことを書いたりもします。


LIRS: 更新の確認にご利用下さい。
カテゴリ: EmacsLisp | Ruby | tDiary | そよかぜ | たべある記 | 羊肉 | 自作ソフト | 花配列 | 超多段シフト | 辛いもの | 雑記 |

2015-09-22 (Tue)

[Ruby][超多段シフト] 風 for Win の辞書から sms.txt 形式データに変換する Ruby スクリプトを修正するついでにオブジェクト指向っぽく書き直した

以前、『風』Ver.2 の辞書をsms.txtにするRubyスクリプトの仮定の間違いらしきところを修正することで7ページ目以降も『風』の仮想鍵盤と一致したことを書いたのだが、時間がとれたしどうせならオブジェクト指向っぽく書き直してみようと考えた。オリジナルのRubyスクリプトのあるWEBページに「Rubyを使っているにもかかわらず、見事なまでに手続き指向なスクリプトですね。:D」と書いてあるのがずっと引っかかっていたもので。

オブジェクト指向っぽくということで、以下のようにイテレータを使うように書き直した。

  1. オリジナルの方では Wind2.reaと Wind2.dicを openしっ放しで whileループでエントリサイズ分のデータを読み込みながら処理しているが、File.binreadでまとめて読み込んでscanメソッドでエントリに分解した後に eachメソッドまたは mapメソッド(current_kanji_tableの作成のところ)で処理するようにした。
  2. オリジナルの方ではyomiのページをwhileループでページ数を更新しながらキーボードに割り当てられた漢字を順に書き出しているが、yomiの全ページ分の文字列を mapメソッドや joinメソッド等で作成してからそれを表示するようにした。

あと細かいところだが、辞書ファイル回りの例外発生処理を追加し、current_kanji_tableを Arrayから Hashに変更している。Arrayから Hashへの変更は、デフォルト値の設定によって値の条件分岐を無くすのが目的。

書き直した結果は以下。仮定の間違いらしきところの修正に対応するコメントには「(重要)」と付けている。本当は作者の Cakeさんに事前に公開の許可をもらおうと思っていたのだけど、オリジナルのスクリプトのあるページから辿って見つかった DTI のメールアドレスは User Unknown だし他の連絡先は見つからないし、ということでいきなり公開してみる。

#!/usr/local/bin/ruby -Ku
# -*- mode: ruby; coding: utf-8 -*-
# coding: utf-8
# 風 for Win の辞書から sms.txt 形式データに変換する Ruby スクリプトの
# 修正ついでにオブジェクト指向っぽく書き直し
# cf. http://home.j00.itscom.net/cake-smd/wiki/KazeIme.html  (original)

READ_DIC_HEADER_SIZE = 0x298  # Wind2.reaのヘッダサイズ、0x280ではない(重要)
READ_DIC_ENTRY_SIZE = 12  # Wind2.reaのエントリサイズ

KANJI_DIC_HEADER_SIZE = 0x50  # Wind2.dicのヘッダサイズ
# Wind2.dicのエントリサイズは固定長でない(重要)

KEY_CODE_TABLE =
  [ 40, 38, 32, 34, 36,   35, 33, 31, 37, 39,
    22, 16,  6, 10, 14,   13,  9,  5, 15, 21,
    18, 12,  2,  4,  8,    7,  3,  1, 11, 17,
    30, 28, 20, 24, 26,   25, 23, 19, 27, 29 ]

def error_conditional( error_message, cond )
  raise error_message  if cond
end

if __FILE__ == $0 then
  error_conditional(  # Wind2.reaのデータサイズチェック
    'Invalid file size of Wind2.rea',
    ( File.size( 'Wind2.rea' ) - READ_DIC_HEADER_SIZE  # ヘッダ除くデータサイズ
    ).modulo( READ_DIC_ENTRY_SIZE ).nonzero? )  # 割り切れなければ例外発生

  # sms.txt表示のためのメインルーチン
  File.binread( 'Wind2.rea', nil, READ_DIC_HEADER_SIZE  # ヘッダ除く全データ
              ).scan( /.{#{READ_DIC_ENTRY_SIZE}}/omn  # Wind2.reaエントリに分解
                     ).each do |wind2rea_entry|
    # 読み仮名 8バイト、配列辞書のオフセット 2バイト、データ長 2バイト(重要)
    yomi, pos, len = wind2rea_entry.unpack( 'A8vv' )

    # yomiデータを読み込んで、
    # 「0バイト以上連続した\xFFと\xFF以外のデータ1バイト」(重要)と
    # 「2バイトデータ(Shift_JIS)」
    # のペア(Wind2.dicエントリ)に分解
    dic_entry_list =  # yomiに対応する Wind2.dicエントリのリスト
      File.binread( 'Wind2.dic', len, KANJI_DIC_HEADER_SIZE + pos  # yomiデータ
                  ).scan( /(\xFF*[^\xFF])(..)/mn )  # Wind2.dicエントリに分解
    error_conditional(  # yomiに対応する dic_entry_listの正当性チェック
      sprintf( 'Invalid entries of %p from pos %d (0x%X) in Wind2.dic',
               yomi, pos, pos ),
      len > dic_entry_list.join( '' ).size )  # 読み飛ばしがあれば例外発生

    # yomiの漢字テーブルの作成
    current_kanji_table = Hash[  # ペアのリストから Hashを生成
      dic_entry_list.map do |key_code_str, kanji_char|
        # key_codeと kanji_charのペアに変換
        [ key_code_str.unpack( 'C*' ).inject( :+ ),  # key_codeを復元
          kanji_char ]
      end ]
    current_kanji_table.default = '  '  # Hashだとこれができる

    # yomiのページの表示
    key_code_max = current_kanji_table.keys.max
    base_max = ( key_code_max.to_f / 40.0 ).ceil
    printf( "#YOMI:%s\n%s", yomi,  # Shift_JISのまま表示
            base_max.times.map do |base|
              KEY_CODE_TABLE.map do |key_code|
                current_kanji_table[ 40 * base + key_code ]
              end.each_slice( 10 ).map do |kanji_line|  # 4分割
                kanji_line.join( '' ) + "\n"
              end.join( '' ) + "#\n"
            end.join( '' ) )

    STDERR.printf(  # 進捗表示
      "yomi: %s, pos: %d (0x%X), size: %d, # of Kanji: %d, max key_code: %d\n",
      yomi, pos, pos, len, current_kanji_table.size, key_code_max )
  end
end

スクリプトの文字コードをUTF-8から変更する場合は、先頭行のオプション「-Ku」(古めのRuby用)、2〜3行目の文字コード記述「coding: utf-8」(それぞれEmacs用、Ruby用)を変更する。

コメントを入れるだけのために改行しなくてもいいところで改行しているところがあるのだか、ちょっとうるさ過ぎたか。



最近のツッコミ:


KND への連絡は以下まで。
m・knd◎zob・jp (「◎」を「@」、「・」を「.」に置換え)