at posts/single.html

Ruby1.9.0 のデフォルトエンコーディング

ちょっと自分用のメモ。 Ruby1.9.0 で起動時の --encoding オプションをいろいろ変えてみた。

# (1) デフォルトエンコーディング
puts Encoding.default_external
# (2) 生成した文字列のエンコーディング
puts "".encoding
# (3) 外部から入力した文字列のエンコーディング
ARGF.each{|v| puts v.encoding }

結果は以下の通り。

encodingオプション(1) デフォルト(2) 生成した文字列(3) 外部から入力した文字列
EUC-JPEUC-JPASCII-8BITEUC-JP
BinaryASCII-8BITASCII-8BITASCII-8BIT

何が言いたいかというと、 --EUC-JP オプションを付けた場合に、以下のコードがエラーになるということ。 これは、 tDiary2.2 の plugin/00default.rb から抜粋してる。

  r = ''
  r << <<-FORM
    <div class="caption"><a name="c">#{comment_description}</a></div>
    <form class="comment" name="comment-form" method="post" action="#{h @index}"><div>
  FORM

なぜダメかというと、 --encoding=EUC-JP とした場合に、変数 r は ASCII-8BIT としてみなされるけど、外部入力である comment_description は EUC-JP とみなされる。 異なるエンコーディング同士の結合なので、「character encodings differ」エラーが起きるというわけ。 これが去年の日記に書いた「ツッコミを入れるとエラーになった」の原因だった。

<追記ここから> 取り消し線の箇所は誤りだった。異なるエンコーディング同士の場合は自動的にエンコードを変換してくれるようになってる。 <追記ここまで>

…と思ったけど、そう単純でもないみたい。 以下のコードは期待通りに動く。 str は ASCII-8BIT で line は EUC-JP なのに、自動的に str を EUC-JP にコンバートしてくれている。

puts Encoding.default_external
str = ''
ARGF.each{|line|
  str << line
  puts str
}

うーん。tDiary のほうは eval しているから?よくわかんない。

追記

原因が分かった。問題は以下の箇所だった。 デフォルトエンコーディング云々は完全に勘違い。

#{comment_name_label}:<input class="field" name="name" value="#{h( @cgi.cookies['tdiary'][0] || '' )}">

ここで、 comment_name_label の encoding は EUC-JP 。 @cgi.cookies['tdiary'][0] の encoding は ASCII-8BIT になってる。 ASCII-8BIT を EUC-JP に変換できなかったからエラーになってたんだ。 でも、 @cgi.cookies だけ encoding が ASCII-8BIT になっている理由はよく分からない。

やっぱり、Ruby 1.9.0 の M17N メモで書かれているように、 EUC-JP じゃなくて Binary モードの方がいいのかなぁ。

ruby 1.8 系と同じように動作させるときには、文字コードをバイナリとみなす(読み込み時に "rb" のように指定するか、 force_encoding(Encoding::Binary) を実行する)といいんじゃないかという気がする。

でも、今のソースを --encoding=Binary で動かすと、以下のエラーが出るんだよね。

character encodings differ(erb):35:in `concat'
(erb):35:in `do_eval_rhtml'
/home/machu/local/lib/ruby/1.9.0/erb.rb:744:in `eval'

関連する日記