Ruby 入門

Ruby ファイル書き込み

多くのアプリケーションにおいて、情報を永続化(Persistence)する能力は、情報を読み取る能力と同じくらい重要です。前章では既存のファイルからデータを読み込む方法に焦点を当てましたが、本章では同様に重要なスキルである「ファイルへのデータ書き込み」について深く掘り下げます。

ユーザーのプリファレンス設定の保存、アプリケーションイベントのログ記録、あるいはデータレポートのエクスポートなど、ファイルへの書き込みによって Ruby プログラムは外部の世界とインタラクトし、プログラム終了後も存在し続けるデータを作成・変更できるようになります。この能力は、小さなユーティリティスクリプトから複雑なデータ管理システムまで、多くの実用的なアプリケーションの基盤となります。

1. 書き込み準備:ファイルをオープンする

データを書き込む前に、まず書き込みを許可する「モード」でファイルをオープンする必要があります。ファイル読み込み時に使用した File.open メソッドは書き込みにも使用されますが、異なるモード引数を渡す必要があります。これらのモードを理解することは非常に重要です。なぜなら、それによってプログラムがファイルとどのようにインタラクトするか、特にファイル内の既存コンテンツをどのように扱うかが決まるからです。

最も一般的に使用される書き込みモードは以下の 2 つです:

  • 'w' (書き込みモード / Write mode):このモードは、ファイルに全く新しい内容を書き込むために使用されます。指定したファイルが存在しない場合、Ruby は自動的にそれを作成します。ファイルが既に存在する場合、その元の内容はすべて「切り詰め(Truncate)」、つまり削除され、Ruby は空のファイルから書き込みを開始します。これは破壊的な操作であるため、使用には十分注意してください!
  • 'a' (追記モード / Append mode):このモードは、新しい内容をファイルの末尾に追加するために使用されます。指定したファイルが存在しない場合、Ruby はそれを作成します。ファイルが既に存在する場合、書き込んだ新しいデータは現在の内容の最後に追加され、元のデータは保持されます。これはログの記録や、既存データを上書きせずに新しいエントリを追加するのに最適です。

ベストプラクティス:読み込み時と同様に、File.open での書き込み時もブロック (do...end) を併用することを強く推奨します。これにより、エラーが発生した場合でもブロック終了後に Ruby が自動的にファイルをクローズ(Close)し、潜在的なリソースリークやデータの破損を防ぐことができます。

1.1 書き込みモード ('w') の使用

書き込みモードでファイルをオープンし、コンテンツを追加する方法を見てみましょう。この操作はファイル内の既存の内容を上書きすることを忘れないでください。

# サンプル 1:新しいファイルを作成するか、既存のファイルを上書きする

# ファイル名を定義
filename = "my_first_output.txt"

# 書き込みモード ('w') でブロックを使用してファイルをオープン
# ブロックを抜ける際、ファイルは自動的にクローズされます。
File.open(filename, 'w') do |file|
  # ブロック内部の 'file' オブジェクトはオープンされたファイルを指します。
  file.write("こんにちは、Rubyの世界!\n") # ファイルに文字列を書き込み、改行コードを含める。
  file.write("これはファイルに書き込んだ最初の行です。\n")
  file.write("もう一行追加してみましょう。\n")
end

puts "内容は書き込みモードで '#{filename}' に保存されました。"

# 検証のためにファイルを読み込む(前章で学んだ方法)
# もしくはスクリプト実行後に直接ファイルを開いて確認してください。
# File.open(filename, 'r') do |file|
#   puts "\n--- '#{filename}' の内容: ---"
#   puts file.read
#   puts "----------------------------------"
# end

my_first_output.txt が存在しなかった場合は新規作成されます。既に存在していた場合は、以前の内容は消去され、今回書き込んだ 3 行のテキストに置き換わります。

1.2 追記モード ('a') の使用

次に、追記モードを探索してみましょう。このモードを使用すると、既存の情報を失うことを心配せずに安全にデータを追加できます。

# サンプル 2:ファイルに内容を追記する

# ファイル名を定義(デモのため同じファイルを使用)
filename = "my_first_output.txt"

# まず、ファイルに初期コンテンツがあることを確認(または作成)
File.open(filename, 'w') do |file|
  file.write("追記デモ用の初期コンテンツ。\n")
end
puts "初期コンテンツが '#{filename}' に書き込まれました。"

# 次に、追記モード ('a') でファイルをオープン
File.open(filename, 'a') do |file|
  file.write("この行は追記されました。\n") # 新しいデータは既存コンテンツの後に追加されます。
  file.write("ここにもう一行の追記コンテンツがあります。\n")
  file.write("数値を計算して追記:#{123 + 456}\n") # 式展開(Interpolation)も可能です。
end
puts "追加コンテンツが '#{filename}' に追記されました。"

# 再読み込みによる検証
# File.open(filename, 'r') do |file|
#   puts "\n--- 追記後の '#{filename}' の内容: ---"
#   puts file.read
#   puts "-------------------------------------------------"
# end

サンプル 2 を実行すると、my_first_output.txt の内容は以下のようになります:

追記デモ用の初期コンテンツ。
この行は追記されました。
ここにもう一行の追記コンテンツがあります。
数値を計算して追記:579

注意点として、write メソッドは文字列の末尾に改行コード (\n) を自動的には追加しません。各データを独立した行にしたい場合は、文字列内に明示的に \n を含める必要があります。

2. write と puts によるデータ書き込み

Ruby はファイルオブジェクトにデータを書き込むためのメソッドをいくつか提供しています。最も一般的なのは writeputs です。これらの違いを理解することが、出力フォーマットを制御する鍵となります。

2.1 write メソッド

write メソッドは、与えられた文字列をファイルの現在位置に直接書き込みます。自動的な改行は行われません。これにより、出力フォーマットを正確にコントロールできます。改行が必要な場合は、自分で追加する必要があります。もし write に文字列以外のオブジェクトを渡すと、それらは自動的に to_s メソッドで文字列に変換されてから書き込まれます。

# サンプル 3:'write' メソッドの使用

filename = "write_example.txt"

File.open(filename, 'w') do |file|
  file.write("行 1、明示的な改行なし。") # 改行は追加されない
  file.write("行 2 は行 1 の直後に続きます。\n") # ここで改行を追加
  file.write("行 3 は新しい行から始まります。\n")
  
  # 数値を書き込む —— 自動的に文字列に変換されます。
  price = 29.99
  quantity = 3
  file.write("合計額:#{price * quantity}\n") # 式展開は文字列を生成します
  file.write(100.to_s + "\n") # 明示的な型変換も可能
end

puts "'write' を使用してコンテンツを '#{filename}' に書き込みました。"

# write_example.txt の出力:
# 行 1、明示的な改行なし。行 2 は行 1 の直後に続きます。
# 行 3 は新しい行から始まります。
# 合計額:89.97
# 100

2.2 puts メソッド

puts メソッド("put string" の略)は、コンソールに出力するために使用する puts と非常に似ています。ファイルオブジェクトで使用すると、指定された文字列(または文字列に変換されたオブジェクト)を書き込み、その後自動的に各項目の末尾に改行コード (\n) を追加します。これは、各項目を 1 行ずつ記述するテキストを作成する際に非常に便利です。

# サンプル 4:'puts' メソッドの使用

filename = "puts_example.txt"

File.open(filename, 'w') do |file|
  file.puts "これは第1行です。" # 'puts' は自動的に改行を追加します
  file.puts "これは第2行です。" # 再び改行を追加
  file.puts "さらに第3行もあります。"
  
  # 'puts' は複数の引数を受け取ることもでき、各引数が新しい行に書き込まれます
  file.puts "アイテム A", "アイテム B", "アイテム C"
  
  # 数値も同様に文字列に変換され、改行が付きます
  file.puts 42
  file.puts Math::PI
end

puts "'puts' を使用してコンテンツを '#{filename}' に書き込みました。"

# puts_example.txt の出力:
# これは第1行です。
# これは第2行です。
# さらに第3行もあります。
# アイテム A
# アイテム B
# アイテム C
# 42
# 3.141592653589793

2.3 write と puts の比較

  • write:きめ細かなコントロールを提供し、自動改行はありません。出力フォーマットを厳密に制御する必要がある場合(例:一行の一部を書き込む、バイナリデータを書き込むなど)に使用します。
  • puts:テキストを一行ずつ書き込むのに便利で、自動的に改行を追加します。各データが独立した行を持つべき標準的なテキストファイルの出力に使用します。

3. 実践的なアプリケーションシナリオ

これらのコンセプトを、実際の一般的なシナリオで実践してみましょう。

3.1 シナリオ 1:アプリケーションログの記録 (Logging)

ユーザーのログインやエラー発生時など、重要なイベントをログとして記録したい場合を想定します。追記モード ('a') は、過去のログ記録を消去したくないため、この要件に最適です。

# シナリオ 1:シンプルなアプリケーションログ記録

def log_event(message, log_file="application.log")
  timestamp = Time.now.strftime("%Y-%m-%d %H:%M:%S")
  log_entry = "[#{timestamp}] #{message}\n" # ログエントリを準備
  
  # 追記モード ('a') でログファイルをオープン
  File.open(log_file, 'a') do |file|
    file.write(log_entry) # ログエントリを書き込む
  end
  puts "ログを記録しました:#{message}"
end

# アプリケーションイベントのシミュレーション
log_event("ユーザー 'alice' がログインしました。")
log_event("禁止されたリソースへのアクセス試行がありました。")
log_event("ユーザー 'bob' の登録が成功しました。")
log_event("エラー:データベース接続に失敗しました。")

puts "\n詳細は 'application.log' を確認してください。"

log_event が呼び出されるたびに、タイムスタンプ付きの新しいメッセージが application.log ファイルの末尾に追加されます。

3.2 シナリオ 2:リストデータの保存

以前学んだ配列(Array)の知識を活かし、ショッピングリストをファイルに保存(1 項目につき 1 行)してみましょう。

# シナリオ 2:配列の項目をファイルに保存

shopping_list = [
  "りんご (Apples)",
  "牛乳 (Milk)",
  "パン (Bread)",
  "卵 (Eggs)",
  "チーズ (Cheese)"
]

output_filename = "shopping_list.txt"

# 書き込みモード ('w') でオープンし、毎回新しいリストを生成
File.open(output_filename, 'w') do |file|
  shopping_list.each do |item|
    file.puts item # puts を使用して各項目に自動改行を追加
  end
end

puts "ショッピングリストを '#{output_filename}' に保存しました。"

これにより shopping_list.txt が作成され、各アイテムが新しい行に配置されます。このスクリプトを複数回実行すると、shopping_list.txt はその時点の配列内容で毎回上書きされます。

3.3 シナリオ 3:基本的な設定情報の保存

シンプルなキー・バリュー(Key-Value)形式の設定情報を保存したい場合があります。実務では YAML や JSON(後の「Gem とライブラリ」で触れる可能性があります)を使用することが多いですが、シンプルな要件ならテキスト形式でも十分です。

# シナリオ 3:シンプルな設定情報の保存

config_settings = {
  "username" => "admin",
  "theme" => "dark",
  "notifications" => "true",
  "items_per_page" => 25
}

config_filename = "app_config.txt"

File.open(config_filename, 'w') do |file|
  config_settings.each do |key, value|
    # 各設定を "key=value" 形式で改行付きで書き込む
    file.puts "#{key}=#{value}"
  end
end

puts "アプリケーション設定を '#{config_filename}' に保存しました。"

これにより、以下のような app_config.txt が生成されます:

username=admin
theme=dark
notifications=true
items_per_page=25

これらの例は、ファイルへのデータ書き込みの基礎技術を示しており、より複雑なファイル操作やデータ永続化機能を構築するための重要な土台となります。