[Ruby]ruby1.8.7から1.9.1に移行したときのメモ(主にM17Nまわりの対応)
追記(2009/09/02): 記事中の「shift_jis」を「Windows-31J」に置き換え。私が普段WindowsXPで「Shift_JIS」と思い込んで使っていた文字コードは「Windows-31J」だったようです。コメントでご指摘いただきました。
今までもそれなりに1.9系を使ってたけど、そろそろ本格的に移行する時期かなと思い作業中。 (ちなみにこのブログはだいぶ前から1.9.1で動かしてる。)
日本語まわりで思いのほか苦労したのでメモしておく。 特にyamlとerb(コマンド)ではまった。
今回の環境ではruby1.8.7を以下のような用途に使っていた。 OSはWindowsXP。
フェーズ1
- Excelをwin32oleを使って読み込んで、シート全体を2次元配列に突っ込む
- 上記の2次元配列をyamlにしてテキストファイルとして保存
フェーズ2
またフェーズ1で生成をしたyamlファイルを次のように処理する。
- rubyスクリプトから上記yamlを読み込んでC言語のコード(主にテーブルデータ)をジェネレート
- erbコマンドを使ってeRubyスクリプトを実行し、この中で上記yamlを読み込んでCコードの断片をジェネレートして埋め込む
フェーズ1、フェーズ2共にrakeで自動化している。
また、扱う文字コードはすべてShift_JIS(SJIS) Windows-31Jである。
RaikfileもSJIS Windows-31Jで記述していた(1.8.7では起動batに多少手を入れている)。
全部SJIS Windows-31Jなのでそんなに苦労しないかと思ったら、全然そんなことなかった。
移行作業その1 〜マジックコメントの追加〜
まず日本語が記述されているrubyスクリプトの先頭にマジックコメントを追加する。
これは単純な話で、日本語はすべてSJIS Windows-31Jを使っているから、SJIS Windows-31Jが記述されているスクリプトの先頭行に以下を追加するだけ。
# -*- coding: Windows-31J -*-
RakefileにもSJIS Windows-31Jを記述しているので、忘れずに上記を追記しておく。
ここは特に問題ない。
移行作業その2 〜「-K」オプションを削除〜
これまで、SJIS Windows-31Jを用いるスクリプトはすべて「-Ks」オプションをつけて実行していたが、
「-K」オプションは1.9でも一応使えるものの非推奨ということなので、一切使用しないことにする。
Rakefileの中でrubyを起動する際に「-Ks」としているところはそれをすべて削除し、スクリプト先頭で、
#!ruby -Ks
としているところもこの行を削除してしまう。 ここの修正も特も問題はない。
移行作業その3 〜erbコマンドへの対応〜
ここで少しはまった。
erbコマンドにも「-Ks」を与えていたのだが、1.9.1のerbコマンドには「-K」オプションがないらしい。
どうやら次の行をeRubyスクリプトの先頭に書き加えることで、マジックコメントと同じ効果が得られるらしい。
<%# -*- coding: Windows-31J -*- %>
うーん、なるほど。 というわけで、上記をeRubyスクリプトに加えておく。
移行作業その4 〜YAML::loadした中身をforce_encodingする〜
ここもはまった。
SJISの文字列をyaml化してloadし直すとエンコーディングがUTF-8になるのだ。
# -*- coding: Windows-31J -*- require 'yaml' str = "あいうえお" p YAML::load(str.to_yaml).encoding #=> #<Encoding:UTF-8>
原因ははっきりわからないけど、yaml化した時点でencodingがなくなっちゃうのかな?
いろいろ試してみた結果は以下。
# -*- coding: Windows-31J -*- require 'yaml' str = "あいうえお" p str #=> "あいうえお" p str.encoding #=> #<Encoding:Windows-31J> p str.to_yaml.encoding #=> #<Encoding:ASCII-8BIT p YAML::load(str.to_yaml) #=> "\x82\xA0\x82\xA2\x82 p YAML::load(str.to_yaml).encoding #=> #<Encoding:UTF-8> p YAML::load(str.to_yaml).force_encoding('Shift_JIS') #=> "あいうえお"
バグの原因に気づくまで時間がかかったが、
YAML::loadで読み込んだ文字列に対してすべて force_encoding('Shift_JIS') force_encoding('Windows-31J')を実行することで解決できた。
移行作業その5 〜機種依存文字を用いたファイル名〜
追記(2009/09/02): マジックコメントの「shift_jis」を「Windows-31J」に置き換えたら次の問題は発生しなくなりました。
ここまでの対応でおおむねうまく動いたのだが、 特定のファイルをFile.openしようとすると次のエラーが発生した。
in `initialize': "\x87@" from Shift_JIS to UTF-8 in conversion from Shift_JIS to Windows-31J (Encoding::UndefinedConversionError)
ファイル名にWindows依存文字の「まる1」が使用されているところでひっかかっているらしい。
# -*- coding: shift_jis -*- File.open('まる1.txt') # エラーになる
ファイル名にそんな文字使うなという話かもしれないけど、 現実にそういうファイル名を作り出す人がいるのだから仕方ない。
とはいえ解決方法がわからないので、 とりあえず、まずい文字を含むファイル名はあらかじめFileUtils.mvで安全な名前にリネームすることにした。
まとめ
今回はyamlとerbを使っていたので、ちょっとはまったけど、これらを使ってなければ割と楽にいけたのかも。
ちなみにエンコーディング絡み以外ではスクリプトを直す必要はひとつもなかった。 これは以前から1.9のコードを意識してたせいかな(最近は意識してなかったんだけど、自然と両対応のコードを書くようになっていたみたい)。 まあエンコーディング以外でははまりどころが少ないということかもね。
1.9.1だとRakefileに普通に日本語が書けたりするのがうれしいね!