Fileクラスを使ったファイルの読み込み処理のうち、個人的に良く使うパターンをテンプレコードとしてまとめた。
前提
- ファイル読み込みでは、実際には読み込んだあとに何らかの処理をすることが多いと思うが、ここではputsで出力するだけ
- 例外発生時は、捕捉した例外クラスとメッセージを出力するだけにする
- カレントディレクトリにlabmen.txtという名前で以下の内容のファイルがあるものとする。
Okabe Rintaro
Shiina Mayuri
Hashida Itaru
Makise Kurisu
Kiryu Moeka
Urushibara Ruka
Feyris
Amane Suzuha
1行ずつ読み込む
IO#each_lineを使うパターン
begin
# File.openはファイルをオープンし、Fileオブジェクトを返す
# 第1引数: ファイルパス
# 第2引数: ファイルモード (デフォルト => 'r')
# 第3引数: ファイルを生成する場合のパーミッション(デフォルト => 0666)
# 失敗した場合にErrno::EXXX例外が発生
#
# File.openにブロックを渡すと、
# ブロックが終了した時点でファイルを自動でクローズする
File.open('labmen.txt') do |file|
# IO#each_lineは1行ずつ文字列として読み込み、それを引数にブロックを実行する
# 第1引数: 行の区切り文字列
# 第2引数: 最大の読み込みバイト数
# 読み込み用にオープンされていない場合にIOError
file.each_line do |labmen|
# labmenには読み込んだ行が含まれる
puts labmen
end
end
# 例外は小さい単位で捕捉する
rescue SystemCallError => e
puts %Q(class=[#{e.class}] message=[#{e.message}])
rescue IOError => e
puts %Q(class=[#{e.class}] message=[#{e.message}])
end
実行結果
Okabe Rintaro
Shiina Mayuri
Hashida Itaru
Makise Kurisu
Kiryu Moeka
Urushibara Ruka
Feyris
Amane Suzuha
IO.foreachを使うパターン
begin
# IO.foreachはファイルの各行を引数としてブロックを繰り返し実行する
# 第1引数: ファイルパス
# 第2引数: 行の区切り文字 (デフォルト => $/)
# オープンに失敗した場合、Errno::EXXX例外が発生
File.foreach('labmen.txt') do |labmen|
# labmenには読み込んだ行が含まれる
puts labmen
end
# 例外は小さい単位で捕捉する
rescue SystemCallError => e
puts %Q(class=[#{e.class}] message=[#{e.message}])
end
実行結果
Okabe Rintaro
Shiina Mayuri
Hashida Itaru
Makise Kurisu
Kiryu Moeka
Urushibara Ruka
Feyris
Amane Suzuha
1度に全体を読み込む
IO#readを使う
begin
File.open('labmen.txt') do |file|
# まずIO#readでファイル全体を文字列として読み込む
# 次にString#splitで改行文字ごとに配列に変換
# 最後にArray#eachで要素ごとにブロックを評価
#
# IO#read
# 第1引数: 読み込むサイズ(デフォルト => nil)
# 第2引数: 出力用のバッファ(デフォルト => '')
# 読み込み用にオープンされていない場合にIOErrorが発生
# データの読み込みに失敗した場合にErrno::EXXXが発生
file.read.split("\n").each do |labmen|
puts labmen
end
end
# 例外は小さい単位で捕捉する
rescue SystemCallError => e
puts %Q(class=[#{e.class}] message=[#{e.message}])
rescue IOError => e
puts %Q(class=[#{e.class}] message=[#{e.message}])
end
実行結果
Okabe Rintaro
Shiina Mayuri
Hashida Itaru
Makise Kurisu
Kiryu Moeka
Urushibara Ruka
Feyris
Amane Suzuha
1文字ずつ読み込む
IO#each_charを使う
begin
# File.openはファイルをオープンし、Fileオブジェクトを返す
# 第1引数: ファイルパス
# 第2引数: ファイルモード (デフォルト => 'r')
# 第3引数: ファイルを生成する場合のパーミッション(デフォルト => 0666)
# 失敗した場合にErrno::EXXX例外が発生
#
# File.openにブロックを渡すと、
# ブロックが終了した時点でファイルを自動でクローズする
File.open('labmen.txt') do |file|
# IO#each_charは文字を1つずつブロックに渡して評価する
# 読み込み用にオープンされていない場合にIOErrorが発生する
file.each_char do |char|
# 改行文字以外の場合、文字の後ろにスペースを入れて出力する
if char != "\n"
print "#{char} "
else
print char
end
end
end
# 例外は小さい単位で捕捉する
rescue SystemCallError => e
puts %Q(class=[#{e.class}] message=[#{e.message}])
rescue IOError => e
puts %Q(class=[#{e.class}] message=[#{e.message}])
end
実行結果
O k a b e R i n t a r o
S h i i n a M a y u r i
H a s h i d a I t a r u
M a k i s e K u r i s u
K i r y u M o e k a
U r u s h i b a r a R u k a
F e y r i s
A m a n e S u z u h a
ファイルロックして読み込む
File#flockを使う
begin
# File.openはファイルをオープンし、Fileオブジェクトを返す
# 第1引数: ファイルパス
# 第2引数: ファイルモード (デフォルト => 'r')
# 第3引数: ファイルを生成する場合のパーミッション(デフォルト => 0666)
# 失敗した場合にErrno::EXXX例外が発生
#
# File.openにブロックを渡すと、
# ブロックが終了した時点でファイルを自動でクローズする
File.open('labmen.txt') do |file|
# File#flockでファイルをロックする
# 第1引数: ロックの種類をFile::Constantsの定数で指定(File::LOCK_EXは排他ロックを意味する)
# 自身がcloseされている場合にIOErrorが発生
# 引数に不正な整数を与えた場合などにErrno::EXXX例外が発生
#
# アンロックはファイルが閉じられるときに自動で行われる
file.flock File::LOCK_EX
file.each_line do |labmen|
puts labmen
end
end
# 例外は小さい単位で捕捉する
rescue SystemCallError => e
puts %Q(class=[#{e.class}] message=[#{e.message}])
rescue IOError => e
puts %Q(class=[#{e.class}] message=[#{e.message}])
end
実行結果
Okabe Rintaro
Shiina Mayuri
Hashida Itaru
Makise Kurisu
Kiryu Moeka
Urushibara Ruka
Feyris
Amane Suzuha
ちなみに、排他ロックされたファイルを別プロセスで排他ロックしようとすると、処理が一時停止してロックが解除されたタイミングで後続の処理が走る。