Ruby では普通コードブロックを用いてファイルにアクセスしますが,複数の処理を一度に行うときにネストしたブロックになり,一見して複雑なコードになります.Javaのようにいくつものイベントストリームをつなげる形で処理すると,興味の無い下位レイヤーのコードを見る必要がなくなります.
また,ストリームオブジェクトのつながりを切り替えるだけで処理が変わるので,メインの処理のコードと,入力の処理に関するコードを分離することができます.そうすると,メインの処理のコードを変えることなく入力を切り替えることができます.
例えば,テキストのcsvを読むときと,gzipされたcsvを読むとき,ブロックベースのコードだとメインの処理をGZipReaderが包むことになるので,圧縮されていないcsvを読む場合とを両方扱おうとしたとき,複雑になってしまいます.ストリームを使えば,file_reader->csv_readerのチェーンを作るか,file_reader -> gzip_reader -> csv_reader のチェーンを作るかの分岐を切り替えるだけですみます.
Javaではむしろこうしたストリームの読み込みがあたりまえですが,Rubyでどうやるのかをまとめている文書を見つけられなかったので,調べた結果を共有します.
■2016-07-26 出力ストリームについても「RubyでJava風ストリーム出力 」に書きました。
準備
以下のファイルを用意しました.
file_path = "abcd.txt"
gzipfile_path = "abcd.txt.gz"
csv_file_path = "abcd.csv"
gzip_csv_file_path = "abcd.csv.gz"
バイト列をjava.io.InputStream風に読み込む
puts "Java 風に InputStream"
file_input_stream = File.new(file_path)
begin
while ch = file_input_stream.read(1)
b_arr = ch.unpack("C*")
b = b_arr[0]
puts "0x#{b.to_s(16)}"
end
ensure
file_input_stream.close
end
文字列を java.io.Reader風に読み込む
puts "Java 風に FileReader"
file_reader = File.new(file_path)
begin
while ! file_reader.eof?
ch = file_reader.readchar
p ch
end
ensure
file_reader.close
end
文字列を java.io.BufferedReader風に読み込む
puts "Java 風に BufferedReader"
file_reader = File.new(file_path)
begin
while ! file_reader.eof?
line = file_reader.readline
puts line
end
ensure
file_reader.close
end
CSVを Reader風にストリームを作って読み込む
puts "Java 風に CSVReader"
require 'csv'
file_reader = File.new(csv_file_path)
csv_reader = CSV.new(file_reader)
begin
while ! csv_reader.eof?
columns = csv_reader.readline
p columns
end
ensure
csv_reader.close
end
Gzip を Reader風にストリームを作って読み込む
puts "Java 風に ストリーム file -> gzip"
require 'zlib'
file_reader = File.new(gzipfile_path)
gzip_reader = Zlib::GzipReader.new(file_reader)
begin
while ! gzip_reader.eof?
line = gzip_reader.readline
puts line
end
ensure
gzip_reader.close
end
Gzip された CSVをReader風にストリームを作って読み込む
puts "Java 風に ストリーム file -> gzip -> csv"
require 'zlib'
require 'csv'
file_reader = File.new(gzip_csv_file_path)
gzip_reader = Zlib::GzipReader.new(file_reader)
csv_reader = CSV.new(gzip_reader)
begin
while ! csv_reader.eof?
columns = csv_reader.readline
p columns
end
ensure
csv_reader.close
end