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"