2019-09-28
IOインスタンスの読み書きモード
Kernel.#open
とIO.open
はだいたい同じ。IO インスタンスを返す。
第二引数で指定する読み書きモードについてメモ。
各種モードの違い
| 読み込み可能 | 書き込み可能 | 開いた時点で
内容を白紙にする | 書き込み
開始位置
—|—|—|—|—
r | 1 | 0 | 0 | -
r+ | 1 | 1 | 0 | 先頭
w | 0 | 1 | 1 | 先頭
w+ | 1 | 1 | 1 | 先頭
a | 0 | 1 | 0 | 末尾
a+ | 1 | 1 | 0 | 末尾
r
デフォルト値。
| 読み込み可能 | 書き込み可能 | 開いた時点で
内容を白紙にする | 書き込み
開始位置
—|—|—|—|—
r | 1 | 0 | 0 | -
_
# 読み込みできる(内容が残っている)
> open('alphabets.txt', 'r') {|f| f.read }
=> "abc\n"
# 書き込みできない
> open('alphabets.txt', 'r') {|f| f.write('def') }
IOError: not opened for writing
r+
| 読み込み可能 | 書き込み可能 | 開いた時点で
内容を白紙にする | 書き込み
開始位置
—|—|—|—|—
r+ | 1 | 1 | 0 | 先頭
_
# 読み込み可能(内容が残っている)
> open('alphabets.txt', 'r+') {|f| f.read }
=> "abc\n"
# 書き込み可能(書き込み位置=先頭から上書きされる)
> open('alphabets.txt', 'r+') {|f| f.write('de') }
=> 2
> open('alphabets.txt', 'r+') {|f| f.read }
=> "dec\n"
w
| 読み込み可能 | 書き込み可能 | 開いた時点で
内容を白紙にする | 書き込み
開始位置
—|—|—|—|—
w | 0 | 1 | 1 | 先頭
_
# 読み込みできない
> open('alphabets.txt', 'w') {|f| f.read }
IOError: not opened for reading
# 書き込みできる(既存の内容は開いた時点で削除されているため、白紙状態に書き込み)
> open('alphabets.txt', 'w') {|f| f.write('de') }
=> 2
> exit
% cat alphabets.txt
=> de%
w+
| 読み込み可能 | 書き込み可能 | 開いた時点で
内容を白紙にする | 書き込み
開始位置
—|—|—|—|—
w+ | 1 | 1 | 1 | 先頭
_
# 読み込みも可能(ただし既存の内容は開いた時点で削除されている)
> open('alphabets.txt', 'w+') {|f| f.read }
=> ""
# 書き込みできる(既存の内容は開いた時点で削除されているため、白紙状態に書き込み)
> open('alphabets.txt', 'w+') {|f| f.write('de') }
=> 2
> exit
% cat alphabets.txt
de%
a
| 読み込み可能 | 書き込み可能 | 開いた時点で
内容を白紙にする | 書き込み
開始位置
—|—|—|—|—
a | 0 | 1 | 0 | 末尾
_
# 読み込みできない
> open('alphabets.txt', 'a') {|f| f.read }
IOError: not opened for reading
# 書き込みできる(末尾に追記)
> open('alphabets.txt', 'a') {|f| f.write('fgh') }
=> 3
> exit
% cat alphabets.txt
defgh%
a+
| 読み込み可能 | 書き込み可能 | 開いた時点で
内容を白紙にする | 書き込み
開始位置
—|—|—|—|—
a+ | 1 | 1 | 0 | 末尾
_
# 読み込みできる
> open('alphabets.txt', 'a+') {|f| f.read }
=> "defgh"
# 書き込みできる(末尾に追記)
> open('alphabets.txt', 'a+') {|f| f.write('ijk') }
=> 3
> exit
% cat alphabets.txt
defghijk%
ファイルポインタに関する考察
IO#pos
は「ファイルポインタの現在の位置」を返す。*1
確かめてみる。
open('alphabets.txt', 'r') {|f| f.read } # => "abcde\n"
open('alphabets.txt', 'r+') do |f|
p f.pos # => 0
p f.gets(2) # => "ab"
p f.pos # => 2
f.write('x')
p f.pos # => 3
end
open('alphabets.txt', 'r') {|f| f.read } # => "abxde\n"
open('alphabets.txt', 'a+') do |f|
p f.pos # => 0
p f.gets(2)
p f.pos # => 2
f.write('x')
p f.pos # => 7
p f.gets # => nil
end
open('alphabets.txt', 'r') {|f| f.read } # => "abxde\nx"
意外だったのは、a+
でもr+
と同様にファイルポインタは先頭から始まり、読み込むにつれて進んでいくことだ。
write したときにr+
だと素直にその時点でのファイルポインタの位置から書き込むが、a+
だと突然 pos が末尾に移動したうえで書き込みをしている模様。
そして書き込みを終えたら元の位置に戻る訳ではなく、末尾のままとなる。そのため読み込むことはできなくなる。
リファレンスなどでもこの辺りの説明は見当たらなかった。 Ruby の実装をみればわかるのだろうが、まだちと難しいので宿題にしておこう。
*1:https://docs.ruby-lang.org/ja/latest/method/IO/i/pos.html