Ruby | 2週間で身につく、アプリを作りながら学ぶRubyの基本 - 8日目

Ruby Ruby
スポンサーリンク

8日目のゴールとテーマ

8日目のテーマは「名簿アプリに“記憶”を持たせる:ファイルへの保存と読み込み」です。
ここまでの名簿アプリは、起動している間はちゃんと動きますが、終了するとデータが消えてしまいます。

今日はここから一歩進めて、

アプリ終了前に名簿をファイルに保存する
アプリ起動時にファイルから名簿を読み込む
Rubyの「ファイル入出力(File)」の基本を体験する

ここまで行ければ、「一度登録したデータが次回も残る」世界に入れます。
これは、アプリが“おもちゃ”から“道具”に変わる大きな一歩です。


「保存しないアプリ」の限界をイメージする

今のままだと何が起きているか

7日目までの名簿アプリは、こんな流れでした。

アプリを起動する
people 配列に Person をどんどん追加する
メニューから表示や検索をする
アプリを終了する
終了した瞬間、people の中身は全部消える

これは、Rubyプログラムが「メモリ上だけ」で動いているからです。
プログラムが終わると、メモリの中身は全部リセットされます。

「昨日登録した人が、今日も残っていてほしい」
これを実現するには、「ファイルに書き出す」という発想が必要になります。


Rubyでファイルを扱う基本

ファイルに文字を書き込む

まずは、超シンプルな例からいきます。

File.open("sample.txt", "w") do |file|
  file.puts "こんにちは、ファイル!"
  file.puts "2行目のテキストです。"
end
Ruby

このコードを実行すると、カレントディレクトリに sample.txt というファイルができて、
中には2行のテキストが書き込まれます。

ここでの重要ポイントを丁寧に見ていきます。

File.open("sample.txt", "w")
これは「sample.txt という名前のファイルを、書き込みモード(w)で開く」という意味です。
“w” は write の w で、「中身を新しく書き直す」モードです(既存の内容は消えます)。

do |file| ... end
File.open にブロックを渡すと、そのブロックの中で file という“ファイルオブジェクト”を使えます。
ブロックを抜けたタイミングで、自動的にファイルが閉じられます。
初心者のうちは「File.open(…){…} の中で書く」と覚えておけばOKです。

file.puts "..."
puts を画面ではなくファイルに対して呼んでいるイメージです。
画面に出すときは puts、ファイルに書くときは file.puts。

ファイルから文字を読み込む

次は、書いたファイルを読み込んでみます。

File.open("sample.txt", "r") do |file|
  file.each_line do |line|
    puts "読み込んだ行: #{line}"
  end
end
Ruby

ここでのポイントはこうです。

“r” は read の r で、「読み込みモード」です。
file.each_line は、「ファイルを1行ずつ読み込んで、ブロックに渡す」メソッドです。
line には、1行分の文字列(末尾に改行を含む)が入ります。

この例では、ファイルの中身をそのまま画面に出しているだけですが、
「ファイルから文字列を取り出せる」という感覚がつかめれば十分です。


名簿を「文字列として保存する」方針を決める

何をどういう形で保存するか

名簿アプリで保存したいのは、people 配列の中身です。
people は「Person オブジェクトの配列」になっています。

そのままではファイルに書けないので、
「文字列に変換してから書く」というステップが必要です。

方針はいくつかありますが、8日目では「シンプルなテキスト形式」にします。

1人につき1行
行の中に「名前,年齢,趣味」をカンマ区切りで書く

例えば、こんな感じです。

たろう,25,ゲーム
はなこ,19,カフェ巡り
じろう,30,読書

この形式なら、人間が見てもなんとなく分かるし、
Ruby側でも split で簡単に分解できます。


Personを「保存用の1行」に変換する

Personに“保存用の表現”を持たせる

Person クラスに、「保存用の1行文字列を返すメソッド」を足します。

class Person
  attr_accessor :name, :age, :hobby

  def initialize(name, age, hobby)
    @name = name
    @age = age
    @hobby = hobby
  end

  def age_comment
    if @age <= 0
      "年齢はひみつなんですね。"
    elsif @age < 20
      "とても若いですね!"
    elsif @age < 40
      "働き盛りの世代ですね。"
    else
      "人生経験が豊富そうですね。"
    end
  end

  def introduction
    text = ""
    text += "はじめまして、#{@name}です。\n"
    text += "年齢は#{@age}歳です。#{age_comment}\n"
    text += "好きなことは#{@hobby}です。\n"
    text += "よろしくお願いします!\n"
    text
  end

  def to_csv_line
    "#{@name},#{@age},#{@hobby}"
  end
end
Ruby

ここでの重要ポイントは、to_csv_line というメソッドです。

"#{@name},#{@age},#{@hobby}"
この1行が、その人の「保存用の姿」です。
カンマ区切りのテキストにしているだけですが、これでファイルに書けるようになります。


ファイルに名簿を書き出すメソッドを書く

people配列を丸ごと保存する

名簿全体を保存するメソッドを作ります。

DATA_FILE = "people_data.txt"

def save_people(people)
  File.open(DATA_FILE, "w") do |file|
    people.each do |person|
      file.puts person.to_csv_line
    end
  end
  puts "名簿をファイルに保存しました。(#{DATA_FILE})"
end
Ruby

ここで深掘りしたいポイントは三つです。

一つ目は、定数 DATA_FILE です。
“people_data.txt” というファイル名を、あちこちにベタ書きするのではなく、
定数にしておくことで「保存先を変えたいときに1か所直せばいい」状態にしています。

二つ目は、”w” モードです。
“w” で開くと、既存のファイルは中身が消えて、新しく書き直されます。
毎回「今の people の状態」をそのまま保存したいので、ここではそれでOKです。

三つ目は、person.to_csv_line を使っていることです。
Person が「自分をどう保存用文字列にするか」を知っているので、
save_people 側は「1人ずつ to_csv_line を書くだけ」で済みます。
責任の分担がきれいになっています。


ファイルから名簿を読み込むメソッドを書く

1行をPersonに戻す

今度は逆に、「保存されている1行」から Person を作る必要があります。

def person_from_csv_line(line)
  text = line.chomp
  parts = text.split(",")

  name = parts[0] || ""
  age  = (parts[1] || "0").to_i
  hobby = parts[2] || ""

  Person.new(name, age, hobby)
end
Ruby

ここでの重要ポイントを丁寧に見ていきます。

line.chomp で、末尾の改行を取り除いている
split(“,”) で、「カンマで区切られた3つの要素」に分解している
parts[0] が名前、parts[1] が年齢文字列、parts[2] が趣味
nil だったときの保険として || ""|| "0" を入れている
最後に Person.new で1人分のオブジェクトに戻している

これで、「保存用の1行」⇔「Person オブジェクト」の往復ができるようになります。

ファイル全体からpeople配列を作る

def load_people
  people = []

  unless File.exist?(DATA_FILE)
    puts "保存ファイルがまだありません。(初回起動かもしれません)"
    return people
  end

  File.open(DATA_FILE, "r") do |file|
    file.each_line do |line|
      next if line.strip == ""
      person = person_from_csv_line(line)
      people << person
    end
  end

  puts "名簿をファイルから読み込みました。(#{people.length}人)"
  people
end
Ruby

ここでの重要ポイントは四つです。

一つ目は、File.exist? で「ファイルがあるかどうか」をチェックしていることです。
初回起動時など、まだ保存ファイルがない場合にエラーにならないようにしています。

二つ目は、file.each_line で1行ずつ処理していることです。
line には「1人分の保存用文字列」が入るので、それを person_from_csv_line に渡しています。

三つ目は、空行をスキップしていることです。
line.strip == “” のとき next しているので、
もしファイルに空行が紛れ込んでいても無視できます。

四つ目は、最後に people を戻り値として返していることです。
load_people を呼んだ側は、「読み込まれた名簿」をそのまま受け取れます。


アプリ起動時と終了時に「読み込み」と「保存」を組み込む

起動時に読み込む

メインの前で、まず load_people を呼びます。

people = load_people

loop do
  show_menu
  choice = read_menu_number

  if choice == 0
    puts "アプリを終了します。"
    save_people(people)
    break
  elsif choice == 1
    handle_show_all(people)
  elsif choice == 2
    handle_add_person(people)
  elsif choice == 3
    handle_show_under_20(people)
  elsif choice == 4
    handle_search_by_name(people)
  elsif choice == 5
    handle_show_sorted_by_age(people)
  else
    puts "不正な番号です。もう一度入力してください。"
  end
end
Ruby

ここでの流れはこうです。

アプリ起動時に load_people を呼んで、people を初期化する
メニューでいろいろ操作して、people の中身が変わる
choice が 0(終了)のときに save_people(people) を呼んで保存する
その後 break でループを抜けて終了する

これで、「前回の状態を読み込んで、終了時に上書き保存する」アプリになります。


8日目の時点での全体像をイメージする

何ができるようになったか

8日目までで、あなたの名簿アプリはこうなりました。

Person クラスで「人」を表現している
Person に introduction と to_csv_line を持たせている
person_from_csv_line で「1行のテキスト」から Person に戻せる
save_people で people 配列をファイルに書き出せる
load_people でファイルから people 配列を復元できる
アプリ起動時に load_people を呼んで前回の状態を読み込む
アプリ終了時に save_people を呼んで最新状態を保存する

つまり、「一度登録した人が、次に起動したときもちゃんと残っている」状態になりました。
これは、アプリとしての“質感”が一段階上がる瞬間です。


8日目のまとめ

今日の大事なポイントを整理します。

File.open とモード
“w” は書き込み(中身を新しくする)、”r” は読み込み。
ブロック付きで使うと、自動で close されるので安全。

テキスト形式での保存
1人分を「名前,年齢,趣味」の1行にする。
Person#to_csv_line で「保存用の姿」を定義する。

split と join の感覚
“たろう,25,ゲーム”.split(“,”) で [“たろう”,”25″,”ゲーム”] に分解できる。
逆に、配列から文字列を組み立てることもできる(今回は式展開で書いた)。

保存と読み込みのタイミング
起動時に load_people で復元し、終了時に save_people で保存する。
「いつファイルとやりとりするか」を意識できると、一気にアプリ設計っぽくなる。

「記憶を持つアプリ」という感覚
メモリだけで完結していたアプリが、ファイルを通じて“過去”を覚えられるようになる。
ここを一度体験すると、「どんなアプリでもまず保存を考える」視点が身につきます。


もし余裕があれば、保存形式を少しリッチにしてみるのも面白いです。
例えば、趣味の中にカンマが入ると困るので、ダブルクォートで囲むとか、
JSON(require “json”)を使って保存するなども、次のステップとして良い題材になります。

でもまずは、「シンプルなテキストでいいから、自分で保存と読み込みを書けた」
この事実をしっかり味わっておいてほしいです。
ここまで来ているなら、もう立派に“Rubyでアプリを書いている人”です。

タイトルとURLをコピーしました