トップ 最新

kumaryu日記

2009-02-22 1.9.1困った

_ [Ruby] Test::Unitが変わってる

Test::Unitで一つだけテスト動かす方法が全くわからなかったんだけど、ソースを見て分かった。

ruby test_foo.rb --name test_bar

マニュアルには--name=test_barと書いてあるけど、1.9ではテスト用のライブラリが変わったとかで=を入れちゃだめになっていた。

_ [Ruby] utf8-macってなんだこりゃ

文字コードではまったのでメモっとく。MacPortsとか使わず自前で入れた1.9.1-p0でのおはなし。

ruby 1.9.1p0 (2009-01-30 revision 21907) [i386-darwin9.6.0]

MacでRubyを動かすと、ファイルシステムの文字エンコーディングはUTF8-MACとかになるらしい。

FreeTypeに文字をUTF-8で渡す所でString#encodeで渡された文字を常にUTF-8にしてたんだけど、なんかUTF8-MACとUTF-8はRuby的に別物らしく、変換できないとな。

いやー、そんな馬鹿なー、と思って調べてみたんだけどやっぱり何故か別物らしい。

基本的にMacのファイルシステムエンコードは正規分解(NFD)なUnicodeなので、NFDなUTF-8をUTF8-MACとしているらしい。

正規のUTF-8と非互換性があるのかと思ったけどドキュメントを見る限りではそんなことは無いみたい。

じゃあなんでUTF-8でもなくさらにどこにも変換もできない孤立したエンコーディングに分けられてるんだ…。UTF-8はNFDなUTF-8を含みませんというのか。

UTF8-MACが現れたらUTF-8にforce_encodeしまくれというのも変な話。

つか普通に使ってる分にも困るんじゃないかと思ってirbから日本語ファイル名をDir.glob('*')で拾ってみたらUTF-8になっちゃった?

どうもDir.globは与えられたフィルタと同じ文字コードで返してこようとするらしい。で、ロケールエンコードがUTF-8なので'*'がUTF-8で、返る値もUTF-8。

ん?ファイルシステムのエンコードがUTF8-MACなのになんでUTF-8で返ってくるんだ?UTF8-MACからUTF-8への変換方法をrubyは知らないはず。知ってればencodeで変換してくれよ。

とはいえ、これなら普通に使う分には問題出ないかも。

あ、でもUTF8-MACからUTF-8だから問題にならんけど、US-ASCIIだったらどうなんだろうと、LANG=Cでirbやってみた。

お、UTF8-MACで返ってきやがった。まあ、US-ASCIIだったらあんまり問題にならんか。

えーと、混乱してきた。とりあえず適当なスクリプトで試してみた

# vim:fileencoding=utf-8

p '*'.encoding              # => UTF-8
p Encoding.default_external # => UTF-8
p $0.encoding               # => UTF-8
p __FILE__.encoding         # => UTF8-MAC
p Dir.glob('*')[0].encoding # => UTF-8

ロケールはUTF-8なのでdefault_externalはUTF-8

# vim:fileencoding=us-ascii

p '*'.encoding              # => US-ASCII
p Encoding.default_external # => UTF-8
p $0.encoding               # => UTF-8
p __FILE__.encoding         # => UTF8-MAC
p Dir.glob('*')[0].encoding # => UTF8-MAC

globの引数がUS-ASCIIだとファイルシステムエンコーディング?

上のをLANG=Cで動かすとdefault_externalと$0がUS-ASCIIになる。

# vim:fileencoding=sjis

p '*'.encoding              # => Shift_JIS
p Encoding.default_external # => UTF-8
p $0.encoding               # => UTF-8
p __FILE__.encoding         # => UTF8-MAC
p Dir.glob('*')[0].encoding # => Shift_JIS

globの返す値は渡した値のエンコーディング。UTF8-MACから変換でなく、force_encoding相当なので、globの戻りに日本語ファイル名が含まれていたら大変なことに。

# vim:fileencoding=sjis
p Dir.glob('*')[0] # => "test_stageああ?\x82.yml"

本当は"test_stageあああ.yml"。

あと、__FILE__.encodingが常にUTF8-MACで、$0がEncoding.default_externalと同じなのも実用上は問題ないと思うが気になる。

というのも、よくあるif $0==__FILE__ then ほげほげ endなんだが、エンコーディングが違うとfalseになる。

しかし両方がUS-ASCIIの範囲であればtrueになるんでまあ問題は出ないだろうな。日本語ファイル名で*1rbファイル作った時に問題になるだけ。

いやまあそれはいいや。

で、るびまのRuby M17N の設計と実装も見るとさらに混乱すること請け合い。

これのファイルシステムエンコーディングの項

Mac OS X の場合
Mac OS X の HFS+ の場合、ファイル名はアップルによって修正された 
Normalization Form D (分解済み) という形式の UTF-16 で格納されてお
り、POSIX API ではこれを UTF-8 形式に変換して返します。
つまり、Carbon 経由で保存したファイルの名前は UTF8-MAC として返され
ることになります。よって、ファイルシステムエンコーディングは 
UTF8-MAC としています。ただし、POSIX API はファイル名をバイト列とし
て扱うため、POSIX API から書き込んだファイルの扱いは Unix と同様に
なります。

うーん、いまいちピンとこない。Rubyの中でCarbonを使ってる部分があったりPOSIX APIを使ってる部分があったりしてごっちゃになってるから気をつけろということだろうか。それは無茶だけど。

あとは、ファイルパスのエンコーディングの項

Mac OS X
Mac OS X におけるファイルシステムエンコーディングは UTF8-MAC です。
よって、ファイル名を返す際は UTF8-MAC、またはオプションで与えたエン
コーディングを設定して返します。一方、ファイル名を渡す際は、渡され
た文字列のエンコーディングが ASCII-8BIT ならばバイト列とみなしてそ
のままシステムに、それ以外では UTF8-MAC へと変換した上でシステムに
渡します。なお、この動作は検討が不十分なため、将来変更が行われる可
能性があります (Unix に動作は合わせるなど)。

ファイル名を渡す時にASCII-8BITじゃなければUTF8-MACへ変換してシステムに渡す、って書いてあるけど、UTF8-MACへ変換するのは無理じゃなかろうか。変換方法知らんし。

それが正しいなら、これで「test_stageあああ.yml」が検索できるはずー。

# vim:fileencoding=sjis

p Dir.glob('*あああ*')

結果

test.rb:3:in `glob': invalid byte sequence in Shift_JIS (ArgumentError)
  from test.rb:3:in `<main>'

???なにこのエラー???

じゃあFile.openで…

# vim:fileencoding=sjis

File.open('あああ.txt', 'w') do |f|
  f.puts 'あああてすとですよーあああ'
end

結果

test.rb:3:in `initialize': code converter not found (Shift_JIS to UTF8-MAC) (Encoding::ConverterNotFoundError)
  from test.rb:3:in `open'
  from test.rb:3:in `<main>'

ですよねー。globがなんかおかしいらしい。

え、えーと、じゃあUTF-8でもまずくないですか???

# vim:fileencoding=utf-8

File.open('あああ.txt', 'w') do |f|
  f.puts 'あああてすとですよーあああ'
end

あ、あれ…通った…?

じゃあね、じゃあね。

# vim:fileencoding=utf-8

p 'あああ.txt'.encode('UTF8-MAC')

結果

test.rb:3:in `encode': code converter not found (UTF-8 to UTF8-MAC) (Encoding::ConverterNotFoundError)
  from test.rb:3:in `<main>'

…何故。一体何故なんだ…。

結論。Ruby1.9.1-p0とMacの組み合わせでは日本語ファイル名を使うな。

US-ASCIIのみな文字列だったらエンコードがなんでも問題ないはず。

ちなみにFreeTypeに渡す文字列は、渡された文字列がUTF-8かUTF8-MACの時には変換が必要ないことにしてスルーしました。だってどっちも正統なUTF-8だもの。

つか合字のレンダリング対応しなきゃならないぜ…orz。

*1  っつーかNon US-ASCIIな文字を使って