Ruby1.9をこれから使う人のためのM17Nまとめ

仙台Ruby会議01で発表した内容のM17Nの部分をまとめました。
これから1.9を使う人の参考になればと思います。

重要なこと

Stringのインスタンス
Encoding情報を持つようになりました
利用者は常にこのことを意識する必要があります。

1.9のStringには次のようなメソッドが追加されています

String#encoding

現在のencodingを返します。

"牛タン".encoding
=> #<Encoding:UTF-8>
String#force_encoding(encoding)

データはそのままで、encoding情報のみ変更します。
バイト列自体は変更されません。
破壊的に変更されます。

"牛タン".force_encoding("EUC-JP")
=> "\xE7\x89\x9B\xE3\x82??\x83\xB3"
String#encode(encoding)
String#encode!(encoding)

他の文字コードに変換します。
!だと破壊的に変換されます。
(仙台Ruby会議01ではencodeも破壊的だと
 言ってしまいましたが間違っていました。)

"牛タン".encode("EUC-JP")
=> "??????"
String#valid_encoding?

入っているバイト列とencodingが合っているかをチェックします

"牛タン".valid_encoding?
=> true
"牛タン".force_encoding("EUC-JP").valid_encoding?
=> false
String#ascii_only?

入っているバイト列が7bit ASCIIのみであるかどうかチェックします

"牛タン".ascii_only?
=> false
"abc123".ascii_only?
=> true

Encodingクラスができた

Encoding情報のクラスが新設されました
Encoding情報はこのクラスのインスタンスとなります。

Encoding.list
Encoding.list
=> [#<Encoding:ASCII-8BIT>, #<Encoding:UTF-8>, #<Encoding:US-ASCII>, #<Encoding:Big5>,以下略

使用できるEncoding情報一覧を出力します

Encoding.find(encoding)

encodingが使えるかどうか検索します。

Encoding.find("EUC-JP")
=> #<Encoding:EUC-JP>
Encoding.find("EUC-J")
ArgumentError: unknown encoding name - EUC-J
	from (irb):15:in `find'
	from (irb):15
	from /Users/fujioka/local/bin/irb19:12:in `<main>'
Encoding.default_external

デフォルトの外部エンコーディングです
1.9ではIOをオープンするときはencoding情報もくっつけます。

f=open("/tmp/hoge.rb","r:euc-jp")

そのときに、省略すると外部エンコーディングが採用されてオープンします。

p open("/tmp/hoge.rb","r").read.encoding
=> #<Encoding:UTF-8>

既存のアプリを1.9に移行するときは外部エンコーディング
何にするかは大変重要です。

Encoding.default_internal

デフォルトの内部エンコーディングです
(なんなのか、うまく説明できません。)
※外部/内部エンコーディングは再代入可能になっていますが、
あまり多用すべきものではないようです。

Encodingの種類

US-ASCII

7bit ASCIIです。

ASCII-8BIT

バイナリです。

その他(UTF-8とかEUC-JPなど)

その他のエンコーディングです。

外部エンコーディングの設定の仕方

コマンドラインオプションで指定
ruby19 -Eeuc-jp sample.rb
shebang行で指定

sample.rbの先頭

#!/usr/bin/env ruby19 -Eeuc-jp
以下略
Encoding.default_external=で代入
#!/usr/bin/env ruby
Encoding.default_external="EUC-JP"
以下略
環境変数など

特に指定しなかった場合は環境変数LANGなどから
設定されます。(おそらく)

なんにもない場合

おそらくUS-ASCIIになります。
CGIで何も指定しない場合は、US-ASCIIになります。<=ハマりの原因

文字列の連結

同じEncodingは連結できます

"ほげ"+"ふが"
=> "ほげふが"

基本的に違うエンコーディングは連結できません。

"ほげ"+"ふが".force_encoding("EUC-JP")
Encoding::CompatibilityError: incompatible character encodings: UTF-8 and EUC-JP
	from (irb):19
	from /Users/fujioka/local/bin/irb19:12:in `<main>'

ただし、7BIT ASCIIのみの文字列は間違えていても連結できます。

"ほげ"+"abc".force_encoding("EUC-JP")
=> "ほげabc"
文字列のdumpについて
"ほげ".dump
=> "\"\\u{307b}\\u{3052}\""
"ほげ".inspect
=> "\"ほげ\""
Marshal.dump("ほげ")
=> "\x04\bI\"\v\xE3\x81\xBB\xE3\x81\x92\x06:\rencoding\"\nUTF-8"

1.9からはStringをdumpして保存するときは、

  • データを保存したいのか
  • Encoding付きで文字列を保存したいのか

を意識する必要があります。

マジックコメント

  • ソースのEncodingを指定するものです。
  • これを指定しないとソースコードにある"ほげ"とかにあたったときに例外になります。
  • ソースの一番上か、shebang行がある場合は2行目に書きます。
emacs風の書き方
# -*- coding: utf-8 -*-
vim風の書き方
# vim:fileencoding=utf-8

どちらでも認識してくれます。

M17Nまとめ

Stringを使うときはEncodingがくっついていることを意識しましょう。
StringをDBなりファイルなりに保存したい時は、
Encodingを保存した方がいいかどうかを意識しましょう。
マジックコメントを忘れないようにしましょう。
(全部マジコメっといてください)