Ruby

Ruby IOクラスについて学ぶ

More than 1 year has passed since last update.

Ruby力をつけるための基礎固めとして、IOクラスについて整理します。※たのしいRubyから


IOクラスとは


プログラムの外部とデータのやりとりをするための機能として入力(Input)と出力(Output)を提供するのが、IOクラスです

| たのしいRuby P339



標準入出力 3つのIOオブジェクト


標準入力


  • キーボードからの入力を受け付ける

  • 組み込み定数STDINに割り当てられる

  • getsなどで標準入力からデータを受け取る


標準出力


  • 標準出力の書き込みは端末画面に向けられている

  • 組み込み定数STDOUTに割り当てられる

  • レシーバを指定しないputs、print、printfなどは標準出力への出力となる


標準エラー出力


  • 標準エラー出力の書き込みは端末画面に向けられている

  • 組み込み定数STDERRに割り当てられる

標準入力が端末からかどうか判定できる

if $stdin.tty?

print "tty true"
else
print "tty false"
end

$ ruby tty.rb

=> "tty true"

$ echo | ruby tty.rb
=> "tty false"


ファイル開閉操作


ファイルを開いて新しいIOオブジェクトを得る



  • io = File.open(file, mode)


  • io = open(file, mode)

modeはどのような目的で開くかを指定する (たのしいRuby P343 表17.1)

mode
意味

"r"
読込専用ファイル

"r+"
読込/書き込み用

"w"
書き込み専用。ファイルがなければ新規作成、既に存在する場合はファイルサイズを0にする

"w+"
読込/書き込み用。その他は"w"と同じ

"a"
追加書き込み専用。ファイルがなければ新規作成

"a+"
読込/追加書き込み専用。ファイルがなければ新規作成


開いたファイルを閉じる



  • io.close

1つのプログラムが開くことができるファイル数には制限があるので、なるべく閉じましょう


File.open + ブロック

File.open に ブロックを渡すと自動的に閉じることができる

File.open("foo.txt") do |io|

while line = io.gets
p line
end
end


IOオブジェクトが閉じられてからどうか確認する



  • io.closed


ファイルのデータを一度に読み込む



  • File.read(file)

ガバっといっきに読み込みます


ファイルの入出力操作


■ 入力操作


IOクラスオブジェクトからデータを1行読み込む



  • io.gets(rs)


  • io.each(rs)


  • io.each_line(rs)

行の区切りは引数(rs)で指定することができる(デフォルトは"¥n")

返却値には改行文字列を含みます

getsメソッドは入力終わりに達してから更に読込とnilを返す

入力終わりまで言ったかどうかは eof? メソッドで判定

while line = io.gets

line.chomp! # 改行文字を削除
puts line
end

io.each_line do |line|

line.chomp! # 改行文字を削除
puts line
end


改行区切りの配列として返却



  • io.readlines(rs)

io.readlines

=> ["aaaa\n", "bbbb\n", "cccc\n", "dddd\n", "eeee\n", "ffff\n", "gggg\n"]


何行まで読み込んだか取得



  • io.lineno


  • io.lineno=(number)

file = File.open("io_sample.txt", "r")

file.each_line do | line |
p file.lineno
p line
end
=>
1
"aaa\n"
2
"bbb\n"
3
"ccc\n"
4
"aaa\n"
5
"bbb\n"
6

lineno=メソッドで値を変更できるが変更してもファイルのポインタは変わりません


1文字ずつ読込みブロックを起動



  • io.each_char

io.each_char do |ch|

p ch
end


1バイトずつ読込みブロックを起動



  • io.each_byte


1文字だけ読込む



  • io.getc

file.getc

=> "a"
file.getc
=> "\n"
file.getc
=> "b"
file.getc
=> "\n"
file.getc
=> "c"
file.getc
=> "\n"
file.getc # 末尾まで行くとnil
=> nil


指定した1文字分を入力バッファへ戻す



  • io.ungetc


1バイトだけ読込む



  • io.getbyte

1バイトだけ読み込んで、バイトに対応するアスキーコードの整数オブジェクトを返却する


指定した1バイト分を入力バッファへ戻す



  • io.ungetbyte

file.getbyte

=> 97
file.ungetbyte(97)
=> nil
file.getbyte
=> 97


一気に読み込む (size指定して読み込む)



  • io.read


■ 出力操作


文字列に改行文字を補って出力



  • io.puts(str0, str1, ...)

file = File.open("io_sample.txt", "r+")

file.puts("hoge", "fuga")

----
cat io_sample.txt
=>
hoge
fuga
aaa
bbb
ccc


指定した文字コードに対応する文字を出力



  • io.putc(ch)

$stdout.putc(99)

=> c
$stdout.putc(81)
=> Q
$stdout.putc(82)
=> R
$stdout.putc("gaga")
=> g


引数に指定した文字列を出力

引数がStringオブジェクト以外は文字列に変換する



  • io.print(str0, str1, ...)


書式指定で出力



  • io.printf(format, arg0, arg1, ...)

$stdout.printf("%s-%d", "ruby", 17)

=> ruby-17


指定の文字列を出力 (1)

引数がStringオブジェクト以外は文字列に変換する

また書き込んだバイト数を返却する



  • io.write(str)

byte_size = $stdout.write(String)

=> String
p byte_size
=> 6


指定の文字列を出力 (2)

戻り値はレシーバ自身



  • io << str

$stdout << 'foo' << 'hoge' << 'hage'

=> foohogehage


ファイルポインタ


IOオブジェクトがファイルのどこを指しているか示す情報 ~ 中略 ~

ファイルポインタは読み書きを行うたびに自動的に進みますが、自分で操作すると、ファイルの中の好きな位置のデータを読み書きできる

| たのしいRuby P350



現在のファイルポインタを取得



  • io.pos


  • io.pos=(position)

pos=メソッドでファイルポインタの位置を変更できる。


ファイルポインタを移動する



  • io.seek(offset, whence)

引数offsetには位置を整数で指定する。

引数whenceにはoffsetをどう評価するかを指定する。

■ whenceの値

- IO::SEEK_SET : offsetで指定された位置にファイルポインタを移動する

- IO::SEEK_CUR : offsetを現在の相対位置とみなしてファイルポインタを移動する

- IO::SEEK_END : offsetをファイルの末尾からの相対位置としてファイルポインタを移動する


ファイルの先頭に戻す



  • io.rewind


ファイルの長さを指定したサイズに切り詰める



  • io.truncate(size)


バイナリモード、テキストモード

ファイルに含まれる改行文字を入力元、出力先に合わせて変換する → テキストモード

変換しない → バイナリモード


バイナリモードに変換する



  • io.binmode


バッファリング


writeメソッドやprintメソッドでIOオブジェクトに対する操作を行うと、プログラムの内部にいったんコピーを作成します。このような領域のことをバッファといいます。 ~ 中略 ~

バッファを使ってデータを処理することをバッファリングといいます

| たのしいRuby P353


2つのコンソール出力のうち標準エラー出力はバッファリングをおこわないため、出力順がコードの順と異なる場合がある


出力バッファにたまっているデータを強制的に出力



  • io.flush


バッファへの書き込みのたびにflushするかどうか


  • io.sync


  • io.sync=(state)

    io.sync = true でバッファの書き込みのたびにflushが呼ばれるようになる



コマンドとのやりとり



  • IO.popen(command, mode)

pattern = Regexp.new(ARGV[0])

filename = ARGV[1]

if /.gz$/ =~ filename
file = IO.popen("gunzip -c #{filename}")
else
file = File.open(filename)
end

file.each_line do |text|
if pattern =~ text
print text
end
end

open(|command, mode)でも同じことができる


open-uri

HTTPやFTPのURLを普通のファイルのように開くことができる

require 'open-uri'

open("http://www.homes.co.jp") do | io |
puts io.read
end
=>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional
.dtd"
>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja">
<head>
<meta http-equiv="Content-Type" content="application/xhtml+xml; charset=utf-8" />
<meta http-equiv="Content-Style-Type" content="text/css" />
<meta http-equiv="Content-Script-Type" content="text/javascript" />
<title>不動産・賃貸・住宅情報(マンション・一戸建て)ならHOME'S【ホームズ】</title>
===
====


stringio

IOオブジェクトのモック

require 'stringio'

io = StringIO.new
io.puts("A")
io.puts("B")
io.puts("C")
io.rewind
p io.read
=>
"A\nB\nC\n"

  • バッファリング
  • コマンドとのやりとり
  • open-uri
  • stringio