Ruby: IO and Encodings

Windowsにおける各種IOのUnicode化について

http://www.garbagecollect.jp/~usa/d/201412b.html#id20141216

http://www.garbagecollect.jp/~usa/d/201412b.html#id20141217

file system encoding

Windowsにおいて、native file system encodingはUTF-16 (NTFSにおいては、正確にはUCS-2)である。Ruby 2.2現在は通常これを`AreFileApisANSI() ? GetACP() : GetOEMCP()`に変換してRubyに返している。

現状、ファイルシステムにアクセスする際には自動的に内部でパス名をUnicode化して対応している(してなければ対応漏れバグ)。また、実際のパス名を返す処理の多くは、引数の文字列があればそのエンコーディングで結果を返すようにしている(引数がなければ上記の通り)。

パス名を取得するほとんどのケースでネタ元となる引数があるはずであり、何も考えずにスクリプトを書けばそれはUTF-8なので、デフォルトをUTF-8に変えても大きな影響はない可能性がある。まずそうなのは Dir.pwd くらい?

少なくともRuby3.0ではデフォルトをUTF-8に変える方向とする。互換性インパクトが小さいなら2.Xでの変更も可能か。

locale encoding

Ruby2.2現在は GetConsoleCP() || GetACP()

Ruby3.0ではデフォルトをUTF-8に変更する。

なお、もう一つのありえるencodingである GetConsoleOutputCP() は現状無視している。これは WriteConsoleW() を使って出力する分には無関係なため。

console

Ruby2.2現在は ReadConsoleInput() / WriteConsoleW()

ReadConsoleInput() のエンコーディングは GetConsoleCP() のもの。すなわち、locale encoding。

locale encodingのデフォルトをUTF-8に変更する時点までに ReadConsoleInputW() に変更する必要がある。ただし、この場合、期待されるエンコーディングへの変換処理は自前でやるのはほぼ不可能で、Ruby I/Oのencoding変換バッファリングシステムに正しく繋いでやる必要がある。

command line

Ruby2.2現在は GetCommandLineW() で取得していたものを、UTF-8でruby.cに渡し、その中でlocale encoding(またはdefault external、項目による)に変換している。結果として、これらのエンコーディングで表現不可能な文字は ‘?’ に変換された状態でスクリプトに渡されている(これは互換性の観点からも仕様と考えてよい)。

将来(Ruby 3.0)でlocale encodingのデフォルト値がUTF-8になっても、このように実装しておくことで対応したことになる(-Eオプションにも対応済みとなる)。

IOの内部・外部エンコーディングについて

ぐちゃぐちゃなのでなんとかしたい。

   /*
    * enc  enc2 read action                      write action
    * NULL NULL force_encoding(default_external) write the byte sequence of str
    * e1   NULL force_encoding(e1)               convert str.encoding to e1
    * e1   e2   convert from e2 to e1            convert str.encoding to e2
    */

   
struct rb_io_enc_t {
       rb_encoding *enc;
       rb_encoding *enc2;
       
int ecflags;
       VALUE ecopts;
   } encs;

IO#set_encoding(enc1 [, enc2] [, opt])

Script Encoding

Normal script case

script encoding

default external

no -K -E, no magic comment

US-ASCII

locale

no -K -E, have magic comment

magic comment

locale

have -E, no magic comment

US-ASCII

-E

have -E, have magic comment

magic comment

-E

have -K, no magic comment

-K

-K

have -K, have magic comment

magic comment

-K

-e and stdin case

script encoding

default external

no -K -E, no magic comment

locale

locale

no -K -E, have magic comment

magic comment

locale

have -E, no magic comment

locale

-E

have -E, have magic comment

magic comment

-E

have -K, no magic comment

-K

-K

have -K, have magic comment

magic comment

-K

https://bugs.ruby-lang.org/projects/ruby-trunk/wiki/ScriptEncoding