Feature #11177
openDATAでEOF文字以降が読めない
Description
遠藤です。
Windows で __END__ 以降に EOF 文字 (\x1A) があったとき、それより先が読めないのは仕様でしょうか。
gen.rb:
puts "p DATA.read"
puts "__END__"
puts "foo\x1Abar"
以下のように実行すると再現します。
> ruby gen.rb > t.rb
> ruby t.rb
"foo"
DATA.binmode.read などとしてみても同じです。
もちろん、Linux では先まで読めます。Windows でも、ソースコードをパイプで流し込んだ場合はなぜか読めます。
> ruby < t.rb
"foo\x1Abar\n"
さらに、EOF 以降に文字がいっぱいあった場合、EOF 以降の一部の文字が抜け落ちるような挙動になるようです。
gen2.rb:
puts "p DATA.read"
puts "__END__"
puts "foo\x1A" + "X" * 8192 + "Z"
> ruby gen2.rb > t.rb
> ruby t.rb
"fooXXXXXXXXXXXXXXXXXXXXXXXXXXXZ\r\n"
バッファリングのバグっぽい挙動ですが、バグでしょうか。
--
Yusuke Endoh mame@ruby-lang.org
        
           Updated by nobu (Nobuyoshi Nakada) over 10 years ago
          Updated by nobu (Nobuyoshi Nakada) over 10 years ago
          
          
        
        
      
      Yusuke Endoh wrote:
Windows で
__END__以降に EOF 文字 (\x1A) があったとき、それより先が読めないのは仕様でしょうか。
text modeでそうなるというのは当然仕様でしょう。
DATA.binmode.readなどとしてみても同じです。
その時点ではもう読んじゃってますから。
もちろん、Linux では先まで読めます。Windows でも、ソースコードをパイプで流し込んだ場合はなぜか読めます。
\x1AがEOFとみなされるのは、ファイルから読んだときだけです。
さらに、EOF 以降に文字がいっぱいあった場合、EOF 以降の一部の文字が抜け落ちるような挙動になるようです。
gen2.rb:
puts "p DATA.read" puts "__END__" puts "foo\x1A" + "X" * 8192 + "Z" > ruby gen2.rb > t.rb > ruby t.rb "fooXXXXXXXXXXXXXXXXXXXXXXXXXXXZ\r\n"バッファリングのバグっぽい挙動ですが、バグでしょうか。
これはDATAを使わなくとも再現しますね。
$ ./ruby -e 'puts "foo\x1A" + "X" * 8192 + "Z"' > txt
$ ./ruby -e 'open("txt"){|f|p f.gets; p f.read}'
"foo"
"XXXXXZ\n"
        
           Updated by mame (Yusuke Endoh) over 10 years ago
          Updated by mame (Yusuke Endoh) over 10 years ago
          
          
        
        
      
      なるほど、メカニズムがよく理解できました。ありがとうございます。
ではその上で質問です。
- 
\x1Aの後のバッファがすっ飛ぶのは仕様でしょうか。(DATAに限らず)
- 
読み込み途中で binmode にすることは非対応でしょうか。 
 もし内部バッファにたまっているのが改行コード変換済みだったら
 無理かなあと思いますが。(ソース読んでなくてすみません)
非対応であれば、例外にしてくれると嬉しいなあと思います。
(これは必要なら別途 feature request にします)
--
Yusuke Endoh mame@ruby-lang.org
        
           Updated by jeremyevans0 (Jeremy Evans) about 2 years ago
          Updated by jeremyevans0 (Jeremy Evans) about 2 years ago
          
          
        
        
      
      - Tracker changed from Bug to Feature
- ruby -v deleted (ruby 2.2.2p95 (2015-04-13 revision 50295) [x64-mingw32])
- Backport deleted (2.0.0: UNKNOWN, 2.1: UNKNOWN, 2.2: UNKNOWN)
        
           Updated by YO4 (Yoshinao Muramatsu) 11 months ago
          Updated by YO4 (Yoshinao Muramatsu) 11 months ago
          
          
        
        
      
      本件についてDATA.pos が__END__直後を示さないこと、Cのfseek(0, SEEK_CUR)っぽく DATA.seek(0, IO:SEEK_CUR)を行っても改善しないことから
単純なワークアラウンドは存在しないと思っていましたが、
3.4-head で試したところ prismでの仕様の違いか別の理由からかは確認できていませんが DATA.pos が __END__直後を指していたので
IO.open(DATA.fileno, "rt") { _1.readlines } # 明示的にuniversal newlineを使用してopen
で\x1A込みで読み込み可能でした。
>less -F data.rb
p IO.open(DATA.fileno, "rt") { _1.readlines }
__END__
textpart1
textpart1^Z
binpart1
binpart2
>ruby data.rb
["textpart1\n", "textpart1\u001A\n", "binpart1\n", "binpart2\n"]
上記にて、data.rb の改行は CR+LF です。
        
           Updated by YO4 (Yoshinao Muramatsu) 11 months ago
          Updated by YO4 (Yoshinao Muramatsu) 11 months ago
          
          
        
        
      
      rubyの-xオプション付与でソースファイルをバイナリオープンさせることでも\x1A以降の読み込みが可能でした。
こちらは ruby 3.3またはそれ以前でも可能のようです。
>less -F data.rb
#!ruby
DATA.pos # posなしだと [] しか得られない
p IO.open(DATA.fileno, "rt") {|f| f.readlines }
__END__
textpart1
textpart1^Z
binpart1
binpart2
>ruby -x data.rb
["textpart1\n", "textpart1\u001A\n", "binpart1\n", "binpart2\n"]
同様の動作を仕込むことで EOF以降も DATAでアクセス可能にできそうですが、DATA.class が Fileでなく IOになっちゃいますね…