🐘

【Ruby】File クラスの扱い

2024/03/31に公開

概要

Ruby の File クラスで行うファイル操作の基本について書いていきます。

CSV など他のファイルを扱うクラスも存在しますが、基本である File の操作を覚えておくと、
大体のノリは同じなので、覚えることが少なくて済むと思います。

環境

❯ ruby -v
ruby 3.2.2 (2023-03-30 revision e51014f9c0) [x86_64-darwin21]

ファイル操作の基本

ファイル操作は、多くの場合以下の処理がセットとなります。

  1. ファイルを開く(または作成する)
  2. 読み取り、書き込みなどのファイル操作
  3. ファイルを閉じる

3の「ファイルを閉じる」は忘れがちですが、これをしないとオープンされたファイルが OS 上でリソースを消費し続けることになります。

これが多数のファイルになると、システムのリソースが枯渇します。
長時間実行されるような Web アプリケーションでは、プロセスが終了しない限り、
このリソースが開放されることはないため、パフォーマンスの低下や、リソースの枯渇などの問題が生じます。

そのため、「ファイルは使ったら閉じる」を意識すること。
Ryby では、close メソッドが、ファイルを閉じる役割をします。

new

File.new では、新しい File オブジェクトを作成します。
これにより、ファイル操作を行うためのオブジェクトが提供されます。

第一引数にファイルのパス、第二引数でモード(読み取り、書き込み)を選択できます。

# 読み取りモードでオブジェクトを作成
file = File.new("./sample_file", "r")
# file.read などのファイル操作
file.close
# 書き込みモードでオブジェクトを作成
file = File.new("./sample_file", "w")
# file.write などのファイル操作
file.close

rモードを指定して、存在しないファイルを指定すると「そんなファイルがないと」怒られます。

> File.new("./hogehoge", "r")
(irb):16:in `initialize': No such file or directory @ rb_sysopen - ./hogehoge (Errno::ENOENT)

ちなみに w モードで存在しないファイルを指定すると、そのファイルが作成されます。

open

File.open では、ファイルを開いて操作するためのオブジェクトを作成します。
new のときと同じく、第一引数にファイルのパス、第二引数でモード(読み取り、書き込み)を選択できます。

# 読み取りモードで開かれた状態のオブジェクトを作成
file = File.open("./sample_file", "r")
# file.read などのファイル操作
file.close
# 読み取りモードで開かれた状態のオブジェクトを作成
file = File.open("./sample_file", "w")
# file.write などのファイル操作
file.close

new メソッドとの違いは、引数にブロックを渡せるということです。
ブロックを渡すと、明示的に close をしなくても良くなるため、実際にはこちらを使うことが多いです。

File.open("sample_file", "r") do |file|
  # file.read などのファイル操作
end

File.open("sample_file", "w") do |file|
  # file.write などのファイル操作
end

read

File.read はその名の通り、指定したファイルの内容を取得します。
例えば以下のようなファイルの場合。

こんにちは
ありがとうございます

これからもよろしくお願いします

結果は以下になります。

> File.read("./sample_file")
=> "こんにちは\nありがとうございます\n\nこれからもよろしくお願いします\n"

delete

File.delete は指定したファイルを削除します。

ユースケース別の操作の紹介

ファイルを新規作成し、書き込み

File.open("sample_file", "w") do |file|
  file.write("1行目\n")
  file.write("2行目\n")
  file.write("3行目\n")
end

# または
File.open("sample_file", "w") do |file|
  file.puts("1行目")
  file.puts("2行目")
  file.puts("3行目")
end

作成されたファイル

sample_file
1行目
2行目
3行目

既存のファイルを読み込み

ファイル全体を読み込み

> File.read("./sample_file")
=> "1行目\n2行目\n3行目\n"

# または
File.open("sample_file", "r") do |file|
  file.read
end
=> "1行目\n2行目\n3行目\n"

1行ずつ読み込み

texts = []
File.open("sample_file", "r") do |file|
  file.each_line do |line|
    texts << line
  end
end

> texts
=> ["1行目\n", "2行目\n", "3行目\n"]

行頭に戻る

ファイルを読み込むと、ファイル内の特定の位置を示す、「ファイルポインタ」というものが更新されていきます。
なので、1度ファイルを読み取ってから、再度読み込むときは、このファイルポインタを戦闘にリセットする必要があります。

そこで使われるのが rewind メソッドです。

results = []

File.open("./sample_file", "r") do |file|
  results << file.read
  results << file.read
end

# 2回目の file.read が空文字になっている。
results
=> ["1行目\n2行目\n3行目\n", ""]
results = []
File.open("./sample_file", "r") do |file|
  results << file.read
  # ファイルポインタを先頭に戻す 
  file.rewind
  results << file.read
end

# 2回目の file.read が取得できている。
results
=> ["1行目\n2行目\n3行目\n", "1行目\n2行目\n3行目\n"]

まとめ

今回は File クラスの扱いについて書きました。
次は CSV, Zlib の扱いに触れたいと思います!

Discussion