Ruby ユーザー入力
インタラクティブなプログラムにおいて、ユーザーとコミュニケーションをとる能力は基本です。前章で学んだファイルの読み書きによってプログラムが永続的なデータと対話できるようになった一方、コマンドラインからのユーザー入力を処理することで、プログラムにリアルタイムで動的な対話能力が備わります。これは、プログラムが実行中に問いかけを行い、回答を受け取り、その内容に応じて自身の振る舞いを調整できることを意味します。この能力は、シンプルなコマンドラインツールから複雑なインタラクティブアプリを開発する上で不可欠な要素であり、あらかじめ用意されたデータを処理するだけのプログラムよりも、あなたの Ruby プログラムをより面白く、実用的なものにしてくれます。
1. gets の使用
インタラクティブなプログラムにおいて、ユーザーからの入力をキャプチャするために、Ruby は非常に直接的なメソッド gets を提供しています。このメソッドは標準入力(通常はキーボード)からユーザーが入力したテキストを1行読み込み、ユーザーが Enter キーを押すまで待機します。読み込まれたテキストは文字列(String)として返されますが、これには Enter キーを押した際に発生する改行コード(\n)が含まれています。
1.1 gets の基本用法
プログラムが gets を呼び出すと実行が一時停止し、ユーザーが内容を入力して Enter を押すのを待ちます。Enter が押されると、gets はその瞬間までに入力されたすべての内容をキャプチャします。
puts "あなたの名前は何ですか?"
user_name = gets
# プログラムはここで停止し、ユーザーの入力を待ちます。
# ユーザーが "Alice" と入力して Enter を押すと、user_name には "Alice\n" が格納されます。
puts "こんにちは、#{user_name}!"
# 改行コードが含まれているため、このコードは「こんにちは、Alice
# !」と出力されます。上の例では、ユーザーが Alice と入力して Enter を押した場合、変数 user_name に含まれる文字列は "Alice\n" になります。末尾の \n が改行コードです。puts が user_name を表示しようとすると、Alice を表示した直後にこの余分な改行コードが出力され、カーソルが次の行に移動します。その上で puts 自身がさらに改行を加えるため、結果として不要な空行やレイアウトの崩れが発生してしまいます。
1.2 chomp による改行の削除
不要な改行コードの問題を解決するために、Ruby の文字列には非常に便利なメソッド chomp が用意されています。chomp メソッドは、文字列の末尾にレコードセパレータ(デフォルトでは \n)が存在する場合、それを取り除きます。実際の開発では、gets の直後にメソッドチェーンで chomp を呼び出すのが非常に一般的な手法です。
puts "あなたの好きな色は何ですか?"
favorite_color = gets.chomp
# ユーザーが "blue" と入力して Enter を押すと、favorite_color には "blue" だけが格納されます。
# 末尾の `\n` 文字が `chomp` によって削除されました。
puts "あなたの好きな色は #{favorite_color} です。"
# これは「あなたの好きな色は blue です。」と出力され、余分な改行は発生しません。ユーザーの純粋な入力内容だけを取得し、末尾の改行を含めたくない場合は、gets.chomp を使用するのが標準的かつ規範的なやり方です。これにより入力文字列がクリーンになり、その後の処理が容易になります。また、出力時のレイアウト崩れや、文字列比較時の予期せぬ不一致を防ぐことができます。
2. ユーザー入力を異なるデータ型へ変換する
ここで覚えておくべき重要なポイントがあります。gets が返す値は常に文字列であるということです。たとえユーザーが数字を入力したとしても、プログラムが受け取るのは文字列フォーマットです。数学的な計算や数値の比較を行うには、この文字列入力を整数(Integer)や浮動小数点数(Float)などの他のデータ型に変換する必要があります。
2.1 to_i による整数への変換
ユーザー入力が整数(Whole number)であることを期待する場合、to_i メソッドを使用して文字列を変換できます。
puts "年齢を入力してください:"
age_as_string = gets.chomp
# ユーザーが "30" と入力した場合、age_as_string は "30"(文字列)です。
age_as_integer = age_as_string.to_i
# age_as_integer は 30(整数)になりました。
puts "5年後、あなたは #{age_as_integer + 5} 歳になります。"
# ここでは数学的演算が正しく実行されます:30 + 5 = 35。
# 入力内容が有効な数字でない場合はどうなるでしょうか?
puts "数字を入力してください:"
not_a_number = gets.chomp # ユーザーが "hello" と入力
converted_value = not_a_number.to_i # converted_value は 0 になります
puts "変換後の値は:#{converted_value}" # 出力:変換後の値は:0to_i が有効な数字で始まらない文字列(例:"hello" や "abc123")に遭遇すると、その文字列を 0 に変換します。文字列が数字で始まっているものの、後ろに非数字文字が続く場合(例:"123abc")、最初の数字部分だけが変換されます(したがって "123abc".to_i は 123 になります)。0 自体が有効な年齢や数量である可能性があるため、この挙動を知っておくことは重要です。無効な入力がエラーにならず 0 として処理されてしまう可能性があるからです。
2.2 to_f による浮動小数点数への変換
同様に、ユーザー入力が小数点を含む数字(浮動小数点数)であることを期待する場合、to_f メソッドを使用します。
puts "身長を入力してください(単位:メートル、例:1.75):"
height_as_string = gets.chomp
# ユーザーが "1.75" と入力した場合、height_as_string は "1.75"(文字列)です。
height_as_float = height_as_string.to_f
# height_as_float は 1.75(浮動小数点数)になりました。
puts "あなたの身長は #{height_as_float} メートルです。"
puts "あなたの身長の2倍は #{height_as_float * 2} メートルです。"
# 浮動小数点数の演算が正しく実行されました。
# to_i と同様に、`to_f` も非数字入力を処理します:
puts "小数を入力してください:"
invalid_float_input = gets.chomp # ユーザーが "twenty-five" と入力
converted_float = invalid_float_input.to_f # converted_float は 0.0 になります
puts "変換後の浮動小数点数は:#{converted_float}" # 出力:変換後の浮動小数点数は:0.0to_i と同じく、to_f も数字以外の文字列を 0.0 に変換します。文字列に数字と他の文字が混在している場合、最初の数字部分だけが解析されます(例:"3.14pi".to_f は 3.14 になります)。
3. ユーザーへのプロンプト表示
優れたユーザーエクスペリエンス(UX)を提供するためには、プログラムがどのような入力を期待しているかをユーザーに明確に伝える必要があります。これは gets を呼び出す前にプロンプト(Prompt:指示文)を表示することで実現します。Ruby には主に puts と print という2つの出力メソッドがあります。
3.1 puts を使ったプロンプト
puts("put string" の略)は指定された文字列を表示し、末尾に改行を加えます。つまり、プロンプトが表示された後、カーソルは次の行に移動します。
# puts を使ったプロンプト
puts "名前を入力してください:"
first_name = gets.chomp # ユーザーは次の行で内容を入力します
puts "こんにちは、#{first_name}!"この場合、ユーザーはプロンプトの1行下で内容を入力することになり、インタフェースに余白が多く感じられることがあります。
3.2 print を使ったプロンプト
print も指定された文字列を表示しますが、末尾に改行を加えません。つまり、プロンプトが表示された後、カーソルは同じ行に留まり、ユーザーはプロンプトのすぐ後ろに入力を行うことができます。
# print を使ったプロンプト
print "苗字を入力してください: " # プロンプトの末尾にスペースを1つ入れています
last_name = gets.chomp # ユーザーはプロンプトと同じ行で入力します
puts "あなたの苗字は #{last_name} です。"コマンドラインアプリケーションでは、ユーザーの回答が質問のすぐ隣に表示されるため、print を使用したほうがより自然な対話の流れを作ることができます。標準的な手法として、print のプロンプト文字列の末尾にスペースを1つ加えることで、プロンプトとユーザーの入力を視覚的に区切るのが一般的です。
4. 複数の入力の処理
ほとんどのインタラクティブなプログラムは、ユーザーから複数の情報を取得する必要があります。必要に応じて「プロンプトを表示 → 入力を取得 → データ型を変換」のパターンを繰り返すだけです。
個人情報を収集するシンプルなプログラムを作成してみましょう:
# ユーザー情報を収集するプログラム
# 1. 名前を尋ねる
print "名前を入力してください: "
first_name = gets.chomp
# 2. 苗字を尋ねる
print "苗字を入力してください: "
last_name = gets.chomp
# 3. 年齢を尋ねて整数に変換
print "年齢を入力してください: "
age = gets.chomp.to_i
# 4. 住んでいる都市を尋ねる
print "居住都市を入力してください: "
city = gets.chomp
# 5. 好きな数字を尋ねて浮動小数点数に変換(小数を許可)
print "好きな数字を入力してください: "
favorite_number = gets.chomp.to_f
puts "\n--- ユーザー情報まとめ ---"
puts "氏名:#{first_name} #{last_name}"
puts "年齢:#{age} 歳"
puts "都市:#{city}"
puts "好きな数字:#{favorite_number}"
# 数値入力の1つを使用して簡単な計算を行う
puts "来年、あなたは #{age + 1} 歳になります。"この例では、順序に従ってユーザーに異なる情報を促しています。各入力はそれぞれの変数に格納されます。gets.chomp で入力文字列をクリーンにし、数値入力が必要な箇所では to_i または to_f を使用しています。これが基礎的なインタラクティブ・セッションの構築方法です。
ユーザー入力を処理する際(特に数値入力)、期待しないデータ(数字が必要な場所にテキストなど)が入力された場合にどうするかを考えるのは、良いプログラミングの習慣です。堅牢な入力バリデーションやエラー処理の詳細は、後の章(具体的には「begin...rescue による基本的なエラー処理」)で解説しますが、条件分岐(第3モジュール:制御フローで学習)を利用して基礎的なチェックを行うこともできます。例えば、to_i 変換後に age が 0 かどうかを確認することで、無効な入力であった可能性を察知できます。