LoginSignup
105

More than 5 years have passed since last update.

Ruby IOクラスについて学ぶ

Last updated at Posted at 2015-09-04

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"

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
105