Jewel-mmo開発日記

RubyでMMORPGを作る過程を記録する日記。 Yokohama.rb 発起人。
2009-08-28

[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

  1. Excelをwin32oleを使って読み込んで、シート全体を2次元配列に突っ込む
  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に普通に日本語が書けたりするのがうれしいね!