From: "U.Nakamura" Date: 2008-05-07T16:41:23+09:00 Subject: [ruby-list:44892] Re: Dir.entriesのエンコーディング (was Re: Ruby 1.9のARGVのエンコーディング) こんにちは、なかむら(う)です。 In message "[ruby-list:44887] Re: Dir.entriesのエンコーディング (was Re: Ruby 1.9のARGVのエンコーディング)" on May.02,2008 18:17:25, wrote: > > Dir.entriesだけを取ってみるならば、こうするのがいけない理由が > > 思いつきません。 > > # が、Dir.globなどに考えを広げるとちょっと悩ましい > > この手の引数や戻り値の encoding を指定する必要があるものは数ありまして、 > それらを考慮に入れるとちょっと慎重な方がいいかなぁと思っています。 慎重に検討すべきであるということには同意します。 ですが、1.9.1までにちゃんと結論を出さないといけないであろうと いうことも思っています。 > open をハッシュに対応させるパッチとかも手元にはあるんですが、 > これも名前的な意味で保留していたりします。 :filesystemencoding => みたいなのですか? > 普通に EUC-JP のパスを渡すと EUC-JP で帰ってくる、とかならいいと思うのですが、 > > [ruby-list:44864]の例(localeがutf-8でファイルエントリがeuc-jp)で、 > > files = Dir.entries(eucpath.force_encoding('utf-8')) > > で UTF-8 なファイルパスの配列が手に入る〜とかだといかがなものか、と。 > > つまるところ、この手の hack のバッドノウハウ化を恐れています。 よろしくないですねえ。 > > Dir.entriesの場合、話に出てくるエンコーディングは3つありえて、 > > (1) 引数であるパス指定のエンコーディング > > (2) 実際のファイルシステム上のエントリのエンコーディング > > (3) 結果配列内の各文字列のエンコーディング > > > * (2)のデフォルトはlocale(rubyが知っている場合はもちろんそ > > れを使う)とし、別途オプション引数等で指定可能とする > > 「rubyが知っている場合はもちろんそれを使う」ということは、 > Encoding.filesystem_encoding のような、 > locale がデフォルトの別の変数または定数が必要ですよね。 ここで「rubyが知ってる場合」というのはプラットフォーム固有の 事情を勘案しています。 そうじゃない場合、rubyは知りようがない、でいいんじゃないか( localeと異なるなら常に指定が必要)と思うんですがどうですか。 > > * もし(1)と異なるエンコーディングで(3)が欲しいという特殊な > > 事態があれば、それはスクリプト作者が(1)をどうにかする > > ここで先述の force_encoding を用いたテクニックを使わせるのには抵抗があります。 そのテクニックを使わせるのには抵抗があるというのは理解できる んですが... > > * (1)がUS-ASCIIでかつ(2)がいわゆるASCII互換でない場合、locale > > またはオプション引数で指定されたエンコーディングを(3)に用 > > いる > > ここの「ASCII互換でない」とは 7bit only ではないということですよね。 > つまり、ファイルシステムのエンコーディングをそのまま渡すと。 これはWindowsの話、つまり(2)がUTF-16であるということを想定し ています(が、7bit onlyじゃない場合を全て含むのか)。 「localeまたはオプション引数で指定されたエンコーディング」は 「ファイルシステムのエンコーディング」ではありません。 これは要するに、Windows上で files = Dir.entries(asciipath) とした時に何が返って来るべきか、ということを述べています。 この場合、(2)、すなわちファイルシステムのエンコーディングは UTF-16であるということをrubyが知っているわけですが、スクリプ ト作者はおそらくUTF-16で結果が欲しいわけではないでしょうし、 互換性の点からもそれは受け入れられないでしょう。 先の私の提案をパターン別で分けると以下のようになります。 なんか無駄に長いです。 (A) ファイルシステムのエンコーディングをrubyが知らないプラッ トフォーム(UNIX等)の場合 (a) ファイルシステムのエンコーディングとlocaleが同じ場合 例: 両方UTF-8 (あ) 引数のエンコーディングがファイルシステムやlocaleと同 じ場合 (ア) 結果のエンコーディングとしてファイルシステムやlocale や引数と同じものが欲しい場合 utf8files = Dir.entiries(utf8path) (イ) 結果のエンコーディングとしてファイルシステムやlocale や引数と異なるものが欲しい場合 例: EUC-JP eucfiles = Dir.entries(utf8path).map{|e|e.encode('euc-jp')} ※結果の明示変換が必要になるので、validでないパス が返る場合に問題あり または eucfiles = Dir.entries(utf8path.encode('euc-jp')) ※引数および結果の暗黙の変換、および、引数の明示変 換が必要になるので、validでないパスを指定する/valid でないパスが返る場合に問題あり (い) 引数のエンコーディングがファイルシステムやlocaleと異 なる場合 例: EUC-JP (ア) 結果のエンコーディングとしてファイルシステムやlocale と同じものが欲しい場合 utf8files = Dir.entiries(eucpath.encode('utf-8')) ※引数の明示変換が必要になるので、validでないパス を指定する場合に問題あり または utf8files = Dir.entiries(eucpath).map{|e|e.encode('utf-8')} ※引数および結果の暗黙の変換、および、結果の明示変 換が必要になるので、validでないパスを指定する/が 返る場合に問題あり (イ) 結果のエンコーディングとして引数と同じものが欲しい 場合 eucfiles = Dir.entries(eucpath) ※引数および結果の暗黙の変換が必要になるので、valid でないパスを指定する/が返る場合に問題 (ウ) 結果のエンコーディングとしてファイルシステムやlocale、 引数とも異なるものが欲しい場合 例: Shift_JIS sjisfiles = Dir.entiries(eucpath.encode('shift_jis')) ※引数および結果の暗黙の変換、および、引数の明示変 換が必要になるので、validでないパスを指定する/が 返る場合に問題あり または sjisfiles = Dir.entiries(eucpath).map{|e|e.encode('shift_jis')} ※引数および結果の暗黙の変換、および、結果の明示変 換が必要になるので、validでないパスを指定する/が 返る場合に問題あり (b) ファイルシステムのエンコーディングとlocaleが異なる場合 例: 前者がEUC-JP、後者がUTF-8 (あ) 引数のエンコーディングがファイルシステムのエンコーデ ィングと同じ場合 (ア) 結果のエンコーディングとしてファイルシステムや引数と 同じものが欲しい場合 eucfiles = Dir.entiries(eucpath, :encoding => 'euc-jp') (イ) 結果のエンコーディングとしてlocaleと同じものが欲し い場合 utf8files = Dir.entries(eucpath, :encoding => 'euc-jp').map{|e|e.encode('utf-8')} ※結果の明示変換が必要になるので、validでないパス が返る場合に問題あり または utf8files = Dir.entries(eucpath.encode('utf-8'), :encoding => 'euc-jp') ※引数および結果の暗黙の変換、および、引数の明示変 換が必要になるので、validでないパスを指定する/が 返る場合に問題あり または(推奨しないが) utf8files = Dir.entries(eucpath.force_encoding('utf-8')) (ウ) 結果のエンコーディングとしてファイルシステムや引数、 localeとも異なるものが欲しい場合 例: Shift_JIS sjisfiles = Dir.entries(eucpath, :encoding => 'euc-jp').map{|e|e.encode('shift_jis')} ※結果の明示変換が必要になるので、validでないパス が返る場合に問題あり または sjisfiles = Dir.entries(eucpath.encode('shift_jis'), :encoding => 'euc-jp') ※引数および結果の暗黙の変換、および、引数の明示変 換が必要になるので、validでないパスを指定する/が 返る場合に問題あり (い) 引数のエンコーディングがlocaleと同じ場合 (ア) 結果のエンコーディングとしてファイルシステムと同じ ものが欲しい場合 eucfiles = Dir.entries(utf8path.encode('euc-jp'), :encoding => 'euc-jp') ※引数の明示変換が必要になるので、validでないパス を指定する場合に問題あり または eucfiles = Dir.entries(utf8path, :encoding => 'euc-jp').map{|e|e.encode('euc-jp')} ※引数および結果の暗黙の変換、および、結果の明示変 換が必要になるので、validでないパスを指定する/が 返る場合に問題あり (イ) 結果のエンコーディングとしてlocaleや引数と同じもの が欲しい場合 utf8files = Dir.entries(utf8path, :encoding => 'euc-jp') ※引数および結果の暗黙の変換が必要になるので、valid でないパスを指定する/が返る場合に問題 (ウ) 結果のエンコーディングとしてファイルシステム、locale や引数とも異なるものが欲しい場合 例: Shift_JIS sjisfiles = Dir.entries(utf8path, :encoding => 'euc-jp').map{|e|e.encode('shift_jis')} ※引数および結果の暗黙の変換、および、結果の明示変 換が必要になるので、validでないパスを指定する/が 返る場合に問題 または sjisfiles = Dir.entries(utf8path.encode('shift_jis'), :encoding => 'euc-jp') ※引数および結果の暗黙の変換、および、引数の明示変 換が必要になるので、validでないパスを指定する/が 返る場合に問題 (う) 引数のエンコーディングがファイルシステム、localeのい ずれとも異なる場合 例: Shift_JIS (ア) 結果のエンコーディングとしてファイルシステムと同じ ものが欲しい場合 eucfiles = Dir.entries(sjispath.encode('euc-jp'), :encoding => 'euc-jp') ※引数の明示変換が必要になるので、validでないパス を指定する場合に問題 または eucfiles = Dir.entries(sjispath, :encoding => 'euc-jp').map{|e|e.encode('euc-jp')} ※引数および結果の暗黙の変換、および、結果の明示変 換が必要になるので、validでないパスを指定する/が 返る場合に問題 (イ) 結果のエンコーディングとしてlocaleと同じものが欲し い場合 utf8files = Dir.entries(sjispath, :encoding => 'euc-jp').map{|e|e.encode('utf-8')} ※引数および結果の暗黙の変換、および、結果の明示変 換が必要になるので、validでないパスを指定する/が 返る場合に問題 または utf8files = Dir.entries(sjispath.encode('utf-8'), :encoding => 'euc-jp') ※引数および結果の暗黙の変換、および、引数の明示変 換が必要になるので、validでないパスを指定する/が 返る場合に問題 (ウ) 結果のエンコーディングとして引数と同じものが欲しい 場合 sjisfiles = Dir.entries(sjispath, :encoding => 'euc-jp') ※引数および結果の暗黙の変換が必要になるので、valid でないパスを指定する/が返る場合に問題 (エ) 結果のエンコーディングとしてファイルシステム、locale、 引数のいずれとも異なるものが欲しい場合 例: UTF-16 utf16files = Dir.entries(sjispath, :encoding => 'euc-jp').map{|e|e.encode('utf-16')} ※引数および結果の暗黙の変換、および、結果の明示変 換が必要になるので、validでないパスを指定する/が 返る場合に問題 または utf16files = Dir.entries(sjispath.encode('utf-16'), :encoding => 'euc-jp') ※引数および結果の暗黙の変換、および、引数の明示変 換が必要になるので、validでないパスを指定する/が 返る場合に問題 (B) ファイルシステムのエンコーディングをrubyが知っているプラ ットフォーム(Windows等)の場合 例: ファイルシステムはUTF-16 (a) ファイルシステムのエンコーディングとlocaleが同じ場合 (あ) 引数のエンコーディングがファイルシステムやlocaleと同 じ場合 (ア) 結果のエンコーディングとしてファイルシステムやlocale や引数と同じものが欲しい場合 utf16files = Dir.entiries(utf16path) (イ) 結果のエンコーディングとしてファイルシステムやlocale や引数と異なるものが欲しい場合 例: EUC-JP eucfiles = Dir.entries(utf16path).map{|e|e.encode('euc-jp')} ※結果の明示変換が必要になるので、validでないパス が返る場合に問題 または eucfiles = Dir.entries(utf16path.encode('euc-jp')) ※引数および結果の暗黙の変換、および、引数の明示変 換が必要になるので、validでないパスを指定する/が 返る場合に問題 (い) 引数のエンコーディングがファイルシステムやlocaleと異 なる場合 例: EUC-JP (ア) 結果のエンコーディングとしてファイルシステムやlocale と同じものが欲しい場合 utf16files = Dir.entiries(eucpath.encode('utf-16')) ※引数の明示変換が必要になるので、validでないパス を指定する場合に問題 または utf16files = Dir.entiries(eucpath).map{|e|e.encode('utf-16')} ※引数および結果の暗黙の変換、および、引数の明示変 換が必要になるので、validでないパスを指定する/が 返る場合に問題 (イ) 結果のエンコーディングとして引数と同じものが欲しい 場合 eucfiles = Dir.entries(eucpath) ※引数および結果の暗黙の変換が必要になるので、valid でないパスを指定する/が返る場合に問題 (ウ) 結果のエンコーディングとしてファイルシステムやlocale、 引数とも異なるものが欲しい場合 例: utf-8 utf8files = Dir.entiries(eucpath).map{|e|e.encode('utf-8')} ※引数および結果の暗黙の変換、および、結果の明示変 換が必要になるので、validでないパスを指定する/が 返る場合に問題 または utf8files = Dir.entiries(eucpath.encode('utf-8')) ※引数および結果の暗黙の変換、および、引数の明示変 換が必要になるので、validでないパスを指定する/が 返る場合に問題 (b) ファイルシステムのエンコーディングとlocaleが異なる場合 例: 後者がWindows-31J (あ) 引数のエンコーディングがファイルシステムのエンコーデ ィングと同じ場合 (ア) 結果のエンコーディングとしてファイルシステムや引数 と同じものが欲しい場合 utf16files = Dir.entiries(utf16path) (イ) 結果のエンコーディングとしてlocaleと同じものが欲し い場合 sjisfiles = Dir.entries(utf16path).map{|e|e.encode('Windows-31J')} ※結果の暗黙の変換、および、結果の明示変換が必要に なるので、validでないパスが返る場合に問題 または sjisfiles = Dir.entries(utf16path.encode('Windows-31J') ※引数および結果の暗黙の変換、および、引数の明示変 換が必要になるので、validでないパスを指定する/が 返る場合に問題 (ウ) 結果のエンコーディングとしてファイルシステムや引数、 localeのいずれとも異なるものが欲しい場合 例: UTF-8 utf8files = Dir.entries(utf16path).map{|e|e.encode('utf-8')} ※結果の明示変換が必要になるので、validでないパス が返る場合に問題 または utf8files = Dir.entries(utf16path.encode('utf-8')) ※引数および結果の暗黙の変換、および、引数の明示変 換が必要になるので、validでないパスを指定する/が 返る場合に問題 (い) 引数のエンコーディングがlocaleと同じ場合 (ア) 結果のエンコーディングとしてファイルシステムと同じ ものが欲しい場合 utf16path = Dir.entries(sjispath.encode('utf-16')) ※引数の明示変換が必要になるので、validでないパス を指定する場合に問題 または utf16path = Dir.entries(sjispath).map{|e|e.encode('utf-16')} ※引数および結果の暗黙の変換、および、結果の明示変 換が必要になるので、validでないパスを指定する/が 返る場合に問題 (イ) 結果のエンコーディングとlocaleや引数と同じものが欲 しい場合 sjispath = Dir.entries(sjispath) ※引数および結果の暗黙の変換が必要になるので、valid でないパスを指定する/が返る場合に問題 (ウ) 結果のエンコーディングとしてファイルシステム、locale や引数のいずれとも異なるものが欲しい場合 例: UTF-8 utf8files = Dir.entries(sjispath).map{|e|e.encode('utf-8')} ※引数および結果の暗黙の変換、および、結果の明示変 換が必要になるので、validでないパスを指定する/が 返る場合に問題 または utf8files = Dir.entries(sjispath.encode('utf-8')) ※引数および結果の暗黙の変換、および、引数の明示変 換が必要になるので、validでないパスを指定する/が 返る場合に問題 (う) 引数のエンコーディングがファイルシステム、localeのい ずれとも異なる場合 例: EUC-JP (ア) 結果のエンコーディングとしてファイルシステムと同じ ものが欲しい場合 utf16files = Dir.entries(eucpath.encode('utf-16')) ※引数の明示変換が必要になるので、validでないパス を指定する場合に問題 または utf16files = Dir.entries(eucpath).map{|e|e.encode('utf-16')} ※引数および結果の暗黙の変換、および、結果の明示変 換が必要になるので、validでないパスを指定する/が 返る場合に問題 (イ) 結果のエンコーディングとしてlocaleと同じものが欲し い場合 sjisfiles = Dir.entries(eucpath).map{|e|e.encode('Windows-31J')} ※引数および結果の暗黙の変換、および、結果の明示変 換が必要になるので、validでないパスを指定する/が 返る場合に問題 または sjisfiles = Dir.entries(eucpath.encode('Windows-31J')) ※引数および結果の暗黙の変換、および、引数の明示変 換が必要になるので、validでないパスを指定する/が 返る場合に問題 (ウ) 結果のエンコーディングとして引数と同じものが欲しい 場合 eucpath = Dir.entries(eucpath) ※引数および結果の暗黙の変換が必要になるので、valid でないパスを指定する/が返る場合に問題 (エ) 結果のエンコーディングとしてファイルシステム、locale、 引数のいずれとも異なるものが欲しい場合 例: UTF-8 utf8files = Dir.entries(eucpath).map{|e|e.encode('utf-8')} ※引数および結果の暗黙の変換、および、結果の明示変 換が必要になるので、validでないパスを指定する/が 返る場合に問題 または utf8files = Dir.entries(eucpath.encode('utf-8')) ※引数および結果の暗黙の変換、および、引数の明示変 換が必要になるので、validでないパスを指定する/が 返る場合に問題 問題のあるパターンの救済方法はどうしたもんですかね。 やっぱダメか、これ? それでは。 -- U.Nakamura