7
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

IOクラスについて($stdin, $stdout, $stderr, etc..)[Ruby]

Last updated at Posted at 2019-01-04

はじめに

IOオブジェクトを扱えるようになるとファイルから必要な情報を抽出したり、加工したりと作業効率化を測ることができます。

IOのあれこれ

IOクラスとは

ファイルに保存されたデータの処理など、プログラムの外部とデータをやりとりするための機能として入力(Input)と出力(Output)を提供します。

標準入出力とは

プログラムを起動すると、3つのIOオブジェクトがあらかじめ割り当てられています。

標準入力

標準入力はデータを受け取るためのIOオブジェクトです。組み込み定数STDINに割り当てられているほか、グローバル変数$stdinからも参照されています。レシーバを指定しないgetsなどのメソッドは、$stdinからデータを受け取ります。標準入力は最初はコンソールに関連づけられていて、キーボードの入力を受け取ります。

標準出力

標準出力はデータを出力するためのIOオブジェクトです。組み込み定数STDOUTに割り当てられているほか、グローバル変数$stdoutからも参照されています。レシーバを指定しないputsprintprintfなどのメソッドは、$stdoutへ出力します。標準出力は最初コンソールに関連づけられています。

標準エラー出力

標準エラー出力は警告やエラー出力するためのIOオブジェクトです。組み込み定数STDERRに割り当てられているほか、グローバル定数$stderrからも参照されています。警告メッセージを表示するためのwarnメソッドが$stderrへ出力します。標準エラー出力も最初はコンソールに関連づけられています。

標準入出力のプログラムでの挙動

$stdout.puts, $stderr.puts

out.rb
3.times do |i|
  $stdout.puts "#{Random.rand}"
  $stderr.puts "#{i+1}回出力しました"
end

出力をファイルにリダイレクトすると、標準出力への書き込みはファイルに書き込まれ、標準エラー出力への書き込みのみが画面に表示されます

$ ruby out.rb > log.txt
1回出力しました
2回出力しました
3回出力しました

log.txtの中身は次のようになっています。きちんと書き込まれていますね。

log.txt
0.3583275954877376
0.6344095584909449
0.862767385471491

出力先を使い分けることで、プログラムの結果として必要なデータはファイルに保存しつつ、処理の進み具合をコンソールに表示することができるようになります

$stdin.tty?

通常、標準入力・標準出力・標準エラー出力は、コンソールに関連づけられています。しかし、コマンドの出力をリダイレクトでファイルに落としたり、あるいはパイプ(|)を使ってほかのプログラムに渡す場合はそうではありません。IOオブジェクトがコンソールに関連づけられているかどうかは、tty?メソッドで判別できます。
TTYという名称はテレタイプ端末(TeleTYpe)に由来している。

tty.rb
if $stdin.tty?
  print "Stdin is a TTY. \n"
else
  print "Stdin is not a TTY. \n"
end
$ ruby tty.rb
Stdin is a TTY.

$ echo | ruby tty.rb
Stdin is not a TTY.

File.open, open

下記のcsvファイルを開封し、ファイルを処理(加工)してみたいと思います。

books.csv
id,"title","created_at","updated_at"
1,"3ステップでしっかり学ぶRuby入門","0000-00-00 00:00:00","0000-00-00 00:00:00"
2,"プロを目指す人のためのRuby入門 言語仕様からテスト駆動開発・デバッグ技法まで","0000-00-00 00:00:00","0000-00-00 00:00:00"
3,"プログラミング言語Ruby","0000-00-00 00:00:00","0000-00-00 00:00:00"
4,"初めてのRuby","0000-00-00 00:00:00","0000-00-00 00:00:00"
5,"メタプログラミングRuby","0000-00-00 00:00:00","0000-00-00 00:00:00"
6,"たのしいRuby","0000-00-00 00:00:00","0000-00-00 00:00:00"
7,"Effective Ruby","0000-00-00 00:00:00","0000-00-00 00:00:00"
8,"たった1日で基本が身につくRuby on Rails超入門","0000-00-00 00:00:00","0000-00-00 00:00:00"
9,"基礎 Ruby on Rails","0000-00-00 00:00:00","0000-00-00 00:00:00"
10,"オブジェクト指向設計実践ガイド - Rubyでわかる 進化し続ける柔軟なアプリケーションの育て方","0000-00-00 00:00:00","0000-00-00 00:00:00"

このファイルの各行先頭20文字まで出力するコードは下記のようになります。

open.rb
File.open('books.csv') do |file|
  while line = file.gets # 行(ライン)が取得できる限り実行
    p line.slice!(0..19)  # 先頭20文字を破壊的に取得
  end
end
$ ruby open.rb
"id,\"title\",\"created_"
"1,\"3ステップでしっかり学ぶRuby入"
"2,\"プロを目指す人のためのRuby入門"
"3,\"プログラミング言語Ruby\",\"0"
"4,\"初めてのRuby\",\"0000-0"
"5,\"メタプログラミングRuby\",\"0"
"6,\"たのしいRuby\",\"0000-0"
"7,\"Effective Ruby\",\""
"8,\"たった1日で基本が身につくRuby"
"9,\"基礎 Ruby on Rails\""
"10,\"オブジェクト指向設計実践ガイド "

このように、ファイルを開いて新しいFileオブジェクトを得るには、File.openメソッドかopenメソッドを使います。つまり、下記のコードに書き換えても同じです。

open.rb
open('books.csv') do |file|
  while line = file.gets
    p line.slice!(0..19)
  end
end

file.close, file.closed?

開いたファイルを閉じるにはcloseメソッドを使います。
1つのプログラムが同時に開くことのできるファイルの数には制限があるので、使い終わったあとはなるべく閉じるようにすべきです。
上のFile.openメソッドを使い、ブロックを渡すと、使い終わったファイルを自動的に閉じることができます。また、このブロックを渡す書き方には、入出力操作の範囲がわかりやすくなるというメリットもあります。

close.rb
file = File.open('books.csv')
p file.closed?
# => false
file.close
p file.closed?
# => true

File.read, File.binread

File.readではFileオブジェクトを作らずにfileからデータを読み込みます。
また、File.binreadではfileをバイナリモードで開いて読み込みます。

read.csv
text = File.read('books.csv')
p text
# => "id,\"title\",\"created_at\",\"updated_at\"\n1,\"3ステップでしっかり学ぶRuby入門\",\"0000-00-00 00:00:00\",\"0000-00-00 00:00:00\"\n2,\"プロを目指す人のためのRuby入門 言語仕様からテスト駆動開発・デバッグ技法まで\",\"0000-00-00 00:00:00\",\"0000-00-00 00:00:00\"\n3,\"プログラミング言語Ruby\",\"0000-00-00 00:00:00\",\"0000-00-00 00:00:00\"\n4,\"初めてのRuby\",\"0000-00-00 00:00:00\",\"0000-00-00 00:00:00\"\n5,\"メタプログラミングRuby\",\"0000-00-00 00:00:00\",\"0000-00-00 00:00:00\"\n6,\"たのしいRuby\",\"0000-00-00 00:00:00\",\"0000-00-00 00:00:00\"\n7,\"Effective Ruby\",\"0000-00-00 00:00:00\",\"0000-00-00 00:00:00\"\n8,\"たった1日で基本が身につくRuby on Rails超入門\",\"0000-00-00 00:00:00\",\"0000-00-00 00:00:00\"\n9,\"基礎 Ruby on Rails\",\"0000-00-00 00:00:00\",\"0000-00-00 00:00:00\"\n10,\"オブジェクト指向設計実践ガイド - Rubyでわかる 進化し続ける柔軟なアプリケーションの育て方\",\"0000-00-00 00:00:00\",\"0000-00-00 00:00:00\"\n"

bintext = File.binread('books.csv')
p bintext
# "id,\"title\",\"created_at\",\"updated_at\"\n1,\"3\xE3\x82\xB9\xE3\x83\x86\xE3\x83\x83\xE3\x83\x97\xE3\x81\xA7\xE3\x81\x97\xE3\x81\xA3\xE3\x81\x8B\xE3\x82\x8A\xE5\xAD\xA6\xE3\x81\xB6Ruby\xE5\x85\xA5\xE9\x96\x80\",\"0000-00-00 00:00:00\",\"0000-00-00 00:00:00\"\n2,\"\xE3\x83\x97\xE3\x83\xAD\xE3\x82\x92\xE7\x9B\xAE\xE6\x8C\x87\xE3\x81\x99\xE4\xBA\xBA\xE3\x81\xAE\xE3\x81\x9F\xE3\x82\x81\xE3\x81\xAERuby\xE5\x85\xA5\xE9\x96\x80 \xE8\xA8\x80\xE8\xAA\x9E\xE4\xBB\x95\xE6\xA7\x98\xE3\x81\x8B\xE3\x82\x89\xE3\x83\x86\xE3\x82\xB9\xE3\x83\x88\xE9\xA7\x86\xE5\x8B\x95\xE9\x96\x8B\xE7\x99\xBA\xE3\x83\xBB\xE3\x83\x87\xE3\x83\x90\xE3\x83\x83\xE3\x82\xB0\xE6\x8A\x80\xE6\xB3\x95\xE3\x81\xBE\xE3\x81\xA7\",\"0000-00-00 00:00:00\",\"0000-00-00 00:00:00\"\n3,\"\xE3\x83\x97\xE3\x83\xAD\xE3\x82\xB0\xE3\x83\xA9\xE3\x83\x9F\xE3\x83\xB3\xE3\x82\xB0\xE8\xA8\x80\xE8\xAA\x9ERuby\",\"0000-00-00 00:00:00\",\"0000-00-00 00:00:00\"\n4,\"\xE5\x88\x9D\xE3\x82\x81\xE3\x81\xA6\xE3\x81\xAERuby\",\"0000-00-00 00:00:00\",\"0000-00-00 00:00:00\"\n5,\"\xE3\x83\xA1\xE3\x82\xBF\xE3\x83\x97\xE3\x83\xAD\xE3\x82\xB0\xE3\x83\xA9\xE3\x83\x9F\xE3\x83\xB3\xE3\x82\xB0Ruby\",\"0000-00-00 00:00:00\",\"0000-00-00 00:00:00\"\n6,\"\xE3\x81\x9F\xE3\x81\xAE\xE3\x81\x97\xE3\x81\x84Ruby\",\"0000-00-00 00:00:00\",\"0000-00-00 00:00:00\"\n7,\"Effective Ruby\",\"0000-00-00 00:00:00\",\"0000-00-00 00:00:00\"\n8,\"\xE3\x81\x9F\xE3\x81\xA3\xE3\x81\x9F1\xE6\x97\xA5\xE3\x81\xA7\xE5\x9F\xBA\xE6\x9C\xAC\xE3\x81\x8C\xE8\xBA\xAB\xE3\x81\xAB\xE3\x81\xA4\xE3\x81\x8FRuby on Rails\xE8\xB6\x85\xE5\x85\xA5\xE9\x96\x80\",\"0000-00-00 00:00:00\",\"0000-00-00 00:00:00\"\n9,\"\xE5\x9F\xBA\xE7\xA4\x8E Ruby on Rails\",\"0000-00-00 00:00:00\",\"0000-00-00 00:00:00\"\n10,\"\xE3\x82\xAA\xE3\x83\x96\xE3\x82\xB8\xE3\x82\xA7\xE3\x82\xAF\xE3\x83\x88\xE6\x8C\x87\xE5\x90\x91\xE8\xA8\xAD\xE8\xA8\x88\xE5\xAE\x9F\xE8\xB7\xB5\xE3\x82\xAC\xE3\x82\xA4\xE3\x83\x89 - Ruby\xE3\x81\xA7\xE3\x82\x8F\xE3\x81\x8B\xE3\x82\x8B \xE9\x80\xB2\xE5\x8C\x96\xE3\x81\x97\xE7\xB6\x9A\xE3\x81\x91\xE3\x82\x8B\xE6\x9F\x94\xE8\xBB\x9F\xE3\x81\xAA\xE3\x82\xA2\xE3\x83\x97\xE3\x83\xAA\xE3\x82\xB1\xE3\x83\xBC\xE3\x82\xB7\xE3\x83\xA7\xE3\x83\xB3\xE3\x81\xAE\xE8\x82\xB2\xE3\x81\xA6\xE6\x96\xB9\",\"0000-00-00 00:00:00\",\"0000-00-00 00:00:00\"\n"

File.write(file, data)

Fileオブジェクトを作らずにfileにdataを書き込みます。

takuyanin.txt
hi, takuyanin 
write.rb
text = "Hello, Takuya!"
File.write("takuyanin.txt", text)
p File.read("takuyanin.txt")
# => Hello, Takuya!

File.write("takuyanin.txt", "!", 5)
p File.read("takuyanin.txt")
# => Hello!, Takuya!

上記のように、第三引数に数値を指定すると、先頭からその数値バイト目以降に書き込み、その後のデータは残ります。
しかし、第3引数を省略した場合はファイルの内容を全てdataに置き換えます

標準入出力の基本操作

eof?

getsメソッドは入力の終わりに達してからさらに読み込むとnilを返します。また、入力の終わりまで読み込んだかどうかを判定したいときは、eof?メソッドで確認できます。
End Of Fileの略です。

open.rbファイルを次のように書き換えます。

open.rb
File.open('books.csv') do |file|
  while line = file.gets
    p line
  end

  p file.eof?
end

$ ruby open.rb
"id,\"title\",\"created_at\",\"updated_at\"\n"
"1,\"3ステップでしっかり学ぶRuby入門\",\"0000-00-00 00:00:00\",\"0000-00-00 00:00:00\"\n"
"2,\"プロを目指す人のためのRuby入門 言語仕様からテスト駆動開発・デバッグ技法まで\",\"0000-00-00 00:00:00\",\"0000-00-00 00:00:00\"\n"
"3,\"プログラミング言語Ruby\",\"0000-00-00 00:00:00\",\"0000-00-00 00:00:00\"\n"
"4,\"初めてのRuby\",\"0000-00-00 00:00:00\",\"0000-00-00 00:00:00\"\n"
"5,\"メタプログラミングRuby\",\"0000-00-00 00:00:00\",\"0000-00-00 00:00:00\"\n"
"6,\"たのしいRuby\",\"0000-00-00 00:00:00\",\"0000-00-00 00:00:00\"\n"
"7,\"Effective Ruby\",\"0000-00-00 00:00:00\",\"0000-00-00 00:00:00\"\n"
"8,\"たった1日で基本が身につくRuby on Rails超入門\",\"0000-00-00 00:00:00\",\"0000-00-00 00:00:00\"\n"
"9,\"基礎 Ruby on Rails\",\"0000-00-00 00:00:00\",\"0000-00-00 00:00:00\"\n"
"10,\"オブジェクト指向設計実践ガイド - Rubyでわかる 進化し続ける柔軟なアプリケーションの育て方\",\"0000-00-00 00:00:00\",\"0000-00-00 00:00:00\"\n"
true

最後にtrueが返ってきてます。

chomp!

chomp!メソッドを使うと、文字列の末尾の改行文字を削除することができます。

chomp.rb
File.open('books.csv') do |file|
  while line = file.gets
    p line.chomp!
  end
end
$ ruby chomp.rb
"id,\"title\",\"created_at\",\"updated_at\""
"1,\"3ステップでしっかり学ぶRuby入門\",\"0000-00-00 00:00:00\",\"0000-00-00 00:00:00\""
"2,\"プロを目指す人のためのRuby入門 言語仕様からテスト駆動開発・デバッグ技法まで\",\"0000-00-00 00:00:00\",\"0000-00-00 00:00:00\""
"3,\"プログラミング言語Ruby\",\"0000-00-00 00:00:00\",\"0000-00-00 00:00:00\""
"4,\"初めてのRuby\",\"0000-00-00 00:00:00\",\"0000-00-00 00:00:00\""
"5,\"メタプログラミングRuby\",\"0000-00-00 00:00:00\",\"0000-00-00 00:00:00\""
"6,\"たのしいRuby\",\"0000-00-00 00:00:00\",\"0000-00-00 00:00:00\""
"7,\"Effective Ruby\",\"0000-00-00 00:00:00\",\"0000-00-00 00:00:00\""
"8,\"たった1日で基本が身につくRuby on Rails超入門\",\"0000-00-00 00:00:00\",\"0000-00-00 00:00:00\""
"9,\"基礎 Ruby on Rails\",\"0000-00-00 00:00:00\",\"0000-00-00 00:00:00\""
"10,\"オブジェクト指向設計実践ガイド - Rubyでわかる 進化し続ける柔軟なアプリケーションの育て方\",\"0000-00-00 00:00:00\",\"0000-00-00 00:00:00\""

each_line

each_lineメソッドは、IOオブジェクトの各行を取得し、処理を行います。

each_line.rb
File.open('books.csv') do |file|
  file.each_line do |line|
    p line.chomp!
  end
end
$ ruby each_line.rb
"id,\"title\",\"created_at\",\"updated_at\""
"1,\"3ステップでしっかり学ぶRuby入門\",\"0000-00-00 00:00:00\",\"0000-00-00 00:00:00\""
"2,\"プロを目指す人のためのRuby入門 言語仕様からテスト駆動開発・デバッグ技法まで\",\"0000-00-00 00:00:00\",\"0000-00-00 00:00:00\""
"3,\"プログラミング言語Ruby\",\"0000-00-00 00:00:00\",\"0000-00-00 00:00:00\""
"4,\"初めてのRuby\",\"0000-00-00 00:00:00\",\"0000-00-00 00:00:00\""
"5,\"メタプログラミングRuby\",\"0000-00-00 00:00:00\",\"0000-00-00 00:00:00\""
"6,\"たのしいRuby\",\"0000-00-00 00:00:00\",\"0000-00-00 00:00:00\""
"7,\"Effective Ruby\",\"0000-00-00 00:00:00\",\"0000-00-00 00:00:00\""
"8,\"たった1日で基本が身につくRuby on Rails超入門\",\"0000-00-00 00:00:00\",\"0000-00-00 00:00:00\""
"9,\"基礎 Ruby on Rails\",\"0000-00-00 00:00:00\",\"0000-00-00 00:00:00\""
"10,\"オブジェクト指向設計実践ガイド - Rubyでわかる 進化し続ける柔軟なアプリケーションの育て方\",\"0000-00-00 00:00:00\",\"0000-00-00 00:00:00\""

each

readlineメソッドを使って一気に終わりまで読んで、各行を要素とする配列を取得し、eachメソッドで加工を施しています(下記の例では、改行文字も削除)。

each.rb
File.open('books.csv') do |file|
  ary = file.readlines
  ary.each do |line|
    p line.chomp!
  end
end
$ ruby each.rb
"id,\"title\",\"created_at\",\"updated_at\""
"1,\"3ステップでしっかり学ぶRuby入門\",\"0000-00-00 00:00:00\",\"0000-00-00 00:00:00\""
"2,\"プロを目指す人のためのRuby入門 言語仕様からテスト駆動開発・デバッグ技法まで\",\"0000-00-00 00:00:00\",\"0000-00-00 00:00:00\""
"3,\"プログラミング言語Ruby\",\"0000-00-00 00:00:00\",\"0000-00-00 00:00:00\""
"4,\"初めてのRuby\",\"0000-00-00 00:00:00\",\"0000-00-00 00:00:00\""
"5,\"メタプログラミングRuby\",\"0000-00-00 00:00:00\",\"0000-00-00 00:00:00\""
"6,\"たのしいRuby\",\"0000-00-00 00:00:00\",\"0000-00-00 00:00:00\""
"7,\"Effective Ruby\",\"0000-00-00 00:00:00\",\"0000-00-00 00:00:00\""
"8,\"たった1日で基本が身につくRuby on Rails超入門\",\"0000-00-00 00:00:00\",\"0000-00-00 00:00:00\""
"9,\"基礎 Ruby on Rails\",\"0000-00-00 00:00:00\",\"0000-00-00 00:00:00\""
"10,\"オブジェクト指向設計実践ガイド - Rubyでわかる 進化し続ける柔軟なアプリケーションの育て方\",\"0000-00-00 00:00:00\",\"0000-00-00 00:00:00\""

lineno

getsメソッドやeach_lineメソッドを使って、行単位で読み込みを行うと、それまでに何行読み込んだかが自動的に記録されます。その行数はlinenoメソッドで取得できます。

lineno.rb
File.open('books.csv') do |file|
  ary = file.readlines
  p "#{file.lineno}行読み込みました"
end
$ ruby lineno.rb
"11行読み込みました"

books.csvファイルの中身は11行だったので、linenoメソッドの挙動が確認できますね。

また次の例も見ていただくとさらにlinenoメソッドの理解が進むかと思います。

lineno2.rb
File.open('books.csv') do |file|
  file.each_line do |line|
    printf("%03d  %s", file.lineno, line)
  end
end
$ ruby lineno2.rb
001  id,"title","created_at","updated_at"
002  1,"3ステップでしっかり学ぶRuby入門","0000-00-00 00:00:00","0000-00-00 00:00:00"
003  2,"プロを目指す人のためのRuby入門 言語仕様からテスト駆動開発・デバッグ技法まで","0000-00-00 00:00:00","0000-00-00 00:00:00"
004  3,"プログラミング言語Ruby","0000-00-00 00:00:00","0000-00-00 00:00:00"
005  4,"初めてのRuby","0000-00-00 00:00:00","0000-00-00 00:00:00"
006  5,"メタプログラミングRuby","0000-00-00 00:00:00","0000-00-00 00:00:00"
007  6,"たのしいRuby","0000-00-00 00:00:00","0000-00-00 00:00:00"
008  7,"Effective Ruby","0000-00-00 00:00:00","0000-00-00 00:00:00"
009  8,"たった1日で基本が身につくRuby on Rails超入門","0000-00-00 00:00:00","0000-00-00 00:00:00"
010  9,"基礎 Ruby on Rails","0000-00-00 00:00:00","0000-00-00 00:00:00"
011  10,"オブジェクト指向設計実践ガイド - Rubyでわかる 進化し続ける柔軟なアプリケーションの育て方","0000-00-00 00:00:00","0000-00-00 00:00:00"

printfメソッドについて(formatなど)は下記記事にまとめております。
format引数でよく見かける'%010d'の実態を解明[Ruby]

each_char

each_charメソッドはIOオブジェクトから1文字ずつデータを読み込んでブロックを実行します。

each_char.rb
File.open('books.csv') do |file|
  file.each_char do |ch|
    p ch
  end
end
$ ruby each_char.rb
"i"
"d"
","
"\""
"t"
"i"
.
.
.

each_byte

IOオブジェクトから1バイトずつデータを読み込んでブロックを起動します。ブロック変数には、読み込んだバイトに対応するASCIIコードを整数値で渡します。

each_byte.rb
File.open('books.csv') do |file|
  file.each_byte do |ch|
    p ch
  end
end
$ ruby each_byte.rb
105
100
44
34
116
.
.
.

getc

IOオブジェクトからデータを1文字だけ読み込みます。ファイルのエンコーディングによっては1文字は複数のバイトから構成される場合もありますが、1文字分を読み込んでStringオブジェクトを返します。入力の終わりに達してからさらに読み込むと、nilを返します。

getc.rb
File.open('books.csv') do |file|
  while ch = file.getc
    p ch
  end
end
$ ruby getc.rb
"i"
"d"
","
"\""
"t"
"i"
.
.
.

ungetc

引数で指定した文字をIOオブジェクトの入力バッファに戻します。

less takuyanin.txt
Hello! Takuya!
ungetc.rb
File.open('takuyanin.txt') do |io|
  p io.getc
  io.ungetc("h")
  p io.gets
end
$ ruby open.rb
"H"
"hello! Takuya!"

おわりに

takuyaninのマイページにRails, Rubyに関していくつか記事をまとめていますので、よければご参考ください。

この記事が役に立ったいう方は、いいね、お願いします(^^)

7
2
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?