こんにちは!じゃいごテックのあつしです。
paizaやAOJなどのプログラミング学習サイトでプログラミングのテストが受けられるのですが、問題を解く以前に標準入力でのデータ受け取りが難しいという話をよく聞きます。
自分も最初のころはかなり苦労したのでまとめてみます。
標準入力とは
実行されているプログラムがデータを受け取る入力元のことです。キーボードやファイルからのデータ入力と思っておけばOK!
文字列で渡されるので、数値として扱うにはデータ型の変更が必要です。
標準出力とは
プログラム処理結果の出力先のことです。画面表示と思っておけばOK!
データ受け取りでよく使うメソッド
とりあえず、下記4つのメソッド (gets, chomp, split, to_i) を覚えておけば、最初のうちは何とかなります。
あまり使わない引数については説明を省略しますが、「オブジェクト指向スクリプト言語 Ruby リファレンスマニュアル」を読んでみると詳しいことが書いてあり、めっちゃ勉強になりますよ!
gets メソッド
gets
1行分を読み込んで、読み込みに成功した時はその文字列を返します。
# 標準入力受け取り # (キーボードで "abc123" と打ってエンターキーを押す) input_str = gets p input_str # > "abc123\n" puts input_str # > abc123
chomp(chomp!) メソッド
"文字列".chomp・"文字列".chomp!
chomp: 末尾の改行コードを取り除いた文字列を返します。
chomp!: 元の文字列末尾から改行コードを取り除いて上書きします。
# 標準入力受け取り # (キーボードで "abc123" と打ってエンターキーを押す) input_str = gets # chompメソッド無しだと受け取った文字列末尾に改行 "\n" が入っている p input_str # > "abc123\n" # chompメソッドは文字列末尾の改行 "\n" を除いた文字列を返す new_str = input_str.chomp p new_str # > "abc123" # chomp!メソッドは末尾の改行を除いて元の文字列を上書きする input_str.chomp! p input_str # > "abc123"
split メソッド
"文字列".split("分割したい文字列") ・ "文字列".split(/正規表現/)
引数で与えた文字または正規表現にマッチする部分で文字列を分割し、配列で返します。引数を省略すると空白文字「\s」で分割します。
空白文字は 「半角スペース」, 「\t」, 「\r」, 「\n」, 「\f」, 「\v」 が該当します。
input_str1 = "one two three" # 引数の文字列で分割した配列を返す ary1 = input_str1.split(" ") p ary1 # > ["one", "two", "three"] # 引数を省略すると空白文字で分割する input_str2 = "one two\nthree four\nfive six" ary2 = input_str2.split p ary2 # > ["one", "two", "three", "four", "five", "six"] # 正規表現で分割も可能 input_str3 = "one and two and three" ary3 = input_str3.split(/ and /) p ary3 # > ["one", "two", "three"]
to_i メソッド
"文字列".to_i(基数)
文字列を引数で与えた基数の整数に変換します。引数を省略すると10進数へ変換します。
# 文字列を整数に変換する input_str1 = "100" new_num1 = input_str1.to_i p new_num1 # > 100 p new_num1.class # > Integer # 末尾に文字列が入っていてもOK input_str2 = "500YEN" new_num2 = input_str2.to_i p new_num2 # > 500 p new_num2.class # > Integer # 先頭が数値以外だと整数値に変換できない # ( 0 に変換される) input_str3 = "$500" new_num3 = input_str3.to_i p new_num3 # > 0
出力でよく使うメソッド
ちょっとだけ標準出力のお話をします。
出力は、基本的には putsメソッド を使い、デバッグ時は pメソッド 、ppライブラリ を使うのが良いかと思います。
puts メソッド
puts(オブジェクト)
引数と改行を出力します。引数に何も与えない場合は改行を出力します。
# 1. 数値も文字も同じように表示(数値は文字列に変換してから表示) n1 = 100 f1 = 0.123 s1 = "abc" puts n1 # > 100 puts f1 # > 0.123 puts s1 # > abc # 2. 改行文字は実際に改行して表示 s2 = "abc\n\rdef\nghi\tjkl\tmno\n" puts s2 # > abc # > def # > ghi jkl mno # 3. 末尾が改行文字の場合は改行を追加しないで出力 s3 = "hoge\n" s4 = "piyo" puts s3 puts s4 # > hoge # > piyo # 4. 配列の要素毎に改行して表示 a1 = ["abc", "def", 123, 456] puts a1 # > abc # > def # > 123 # > 456 # 5. 多次元配列は平坦化して表示 a2 = [[["ABC", "DEF"], ["GHI", "JKL"]], [[12, 34], [56, 78]]] puts a2 # > ABC # > DEF # > GHI # > JKL # > 12 # > 34 # > 56 # > 78 # 6. ハッシュは中身をそのまま表示 h1 = {"a"=>1, "b"=>2, "c"=>3} puts h1 # > {"a"=>1, "b"=>2, "c"=>3}
p メソッド、pp ライブラリ
p(オブジェクト) ・ pp(オブジェクト)
引数を人間が読みやすい形に整形して出力して改行します。
ppライブラリは、基本的にはpメソッドと同じ動作ですが、さらに見やすい形に整形して表示します。
# p メソッド # 1. 数値はそのまま表示, 文字列はダブルクォートで囲って表示 n1 = 100 f1 = 0.123 s1 = "abc" p n1 # > 100 p f1 # > 0.123 p s1 # > "abc" # 2. 改行文字は改行文字のまま表示 s2 = "abc\ndef\nghi\tjkl\tmno\n" p s2 # > "abc\n\rdef\nghi\tjkl\tmno\n" # 3. 配列はそのまま表示 a1 = ["abc", "def", 123, 456] p a1 # > ["abc", "def", 123, 456] # 4. 多次元配列もそのまま表示 a2 = [[["ABC", "DEF"], ["GHI", "JKL"]], [[12, 34], [56, 78]]] p a2 # > [[["ABC", "DEF"], ["GHI", "JKL"]], [[12, 34], [56, 78]]] # 5. ハッシュもそのまま表示 h1 = {"a"=>1, "b"=>2, "c"=>3} p h1 # > {"a"=>1, "b"=>2, "c"=>3}
# pp ライブラリ # 1. 改行文字を含んだ文字列を見やすく整形 s1 = "abc\ndef\nghi\tjkl\tmno\n" p s1 # > "abc\n\rdef\nghi\tjkl\tmno\n" pp s1 # > "abc\n" + "def\n" + "ghi\tjkl\tmno\n" # 2. 要素数が多い配列やハッシュを見やすく整形 a1 = [ [11, 12, 13, 14, 15, 16, 17, 18, 19, 20], [21, 22, 23, 24, 25, 26, 27, 28, 29, 30], [31, 32, 33, 34, 35, 36, 37, 38, 39, 40], [41, 42, 43, 44, 45, 46, 47, 48, 49, 50], [51, 52, 53, 54, 55, 56, 57, 58, 59, 60] ] # p だとちょっと見づらい p a1 # > [[11, 12, 13, 14, 15, 16, 17, 18, 19, 20], [21, 22, 23, 24, 25, 26, 27, 28, 29, 30], [31, 32, 33, 34, 35, 36, 37, 38, 39, 40], [41, 42, 43, 44, 45, 46, 47, 48, 49, 50], [51, 52, 53, 54, 55, 56, 57, 58, 59, 60]] # pp は見やすく整形 pp a1 # > [[11, 12, 13, 14, 15, 16, 17, 18, 19, 20], # > [21, 22, 23, 24, 25, 26, 27, 28, 29, 30], # > [31, 32, 33, 34, 35, 36, 37, 38, 39, 40], # > [41, 42, 43, 44, 45, 46, 47, 48, 49, 50], # > [51, 52, 53, 54, 55, 56, 57, 58, 59, 60]]
実際にデータを受け取ってみよう!
データが1個の場合
gets
の後にchomp
を付けない場合、改行コードが付いたままになります。
標準入力 |
taro |
input_str = gets p input_str # > "taro\n"
gets
の後にchomp
を付けると、改行コードが除去されます。文字列受け取りの際は、とりあえずchomp
を付けておけば間違いが少ないと思います。
標準入力 |
taro |
input_str = gets.chomp p input_str # > "taro"
getsを使ってpaiza練習問題を解いてみる
1つのデータの入力 (paizaランク D 相当)
※ paiza レベルアップ問題集 標準入力サンプル問題セット より
問題:
標準入力で1つの文字列が与えられるので、それを入力して、そのまま1行で出力してください。
入力される値
文字列Sが1行で入力されます。
期待する出力
1行での出力
条件
すべてのテストケースにおいて、以下の条件をみたします。
- Sは1文字以上100文字以下の文字列
- Sの各文字は英小文字または大文字または数字
入力例1
abc
出力例1
abc
入力例2
a
出力例2
a
入力例3
2000
出力例3
2000
入力例4
0x04a12bE
出力例4
0x04a12bE
解答例:
# [解答例1] # 入力をそのまま出力するだけなのでこれでOK puts gets # [解答例2] # chompをつけてもOK # puts は末尾に改行が無い場合自動で改行を入れてくれる puts gets.chomp
データが1行で半角スペース区切りの場合
半角スペース区切りなので gets.split(" ")
か、引数を省略して gets.split
でもOK!
標準入力 |
taro jiro hanako |
input_ary = gets.split(" ") p input_ary # > ["taro", "jiro", "hanako"]
splitを使ってpaiza練習問題を解いてみる
1行のデータの入力 (paizaランク D 相当)
※ paiza レベルアップ問題集 標準入力サンプル問題セット より
問題:
標準入力で1行の文字列が与えられるので、それを入力して、そのまま1行で出力してください。
入力される値
文字列Sが1行で入力されます。
期待する出力
1行での出力
条件
すべてのテストケースにおいて、以下の条件をみたします。
- Sは1文字以上100文字以下の文字列
- Sの各文字は英小文字または大文字または数字または半角スペース
入力例1
abc
出力例1
abc
入力例2
a b c
出力例2
a b c
入力例3
2000
出力例3
2000
入力例4
0x04a12bE
出力例4
0x04a12bE
解答例:
# [解答例1] 1行をそのまま出力なので1個の入力と同じで OK puts gets # [解答例2] # 通常は受け取り後に何か処理がある場合が殆どなので # 半角スペース区切りの配列で受け取る方法が便利です # 入力例2 "a b c\n" # input_ary = gets.split でもOK input_ary = gets.chomp.split # joinメソッドで配列を半角スペースで結合して表示 puts input_ary.join(" ") # > a b c
1行目にデータ数nが与えられ、続いてn行のデータがある場合
for文を使って書くことも出来ますが、Rubyではtimesメソッドを使った方が自然だと思います。
標準入力 |
3 |
taro |
jiro |
hanako |
# [ 例1 ] times メソッドで書く場合 n = gets.to_i names = [] # n 回の繰り返し処理 n.times do names.push(gets.chomp) end p names # > ["taro", "jiro", "hanako"]
# [ 例2 ] for 文を使う場合(Ruby で for 文はあまり使わない) n = gets.to_i names = [] # n 回の繰り返し処理 for _ in 1..n names.push(gets.chomp) end p names # > ["taro", "jiro", "hanako"]
timesメソッドを使ってpaiza練習問題を解いてみる
N行のデータの入力 (paizaランク D 相当)
※ paiza レベルアップ問題集 標準入力サンプル問題セット より
問題:
標準入力でN行それぞれで文字列が与えられるので、それらを入力して、順にそのままN行で出力してください。
入力される値
1行目でNが与えらます。
続くN行の各行で文字列が与えられます。
期待する出力
N行での出力
条件
すべてのテストケースにおいて、以下の条件をみたします。
- 1 <= N <= 10
- 入力される各文字列は1文字以上100文字以下の文字列
- 入力される各文字列の各文字は英小文字または大文字または数字または半角スペース
入力例1
3
aaaaa
bbbbbb
cccc
出力例1
aaaaa
bbbbbb
cccc
入力例2
1
a b c
出力例2
a b c
入力例3
5
4
3
2
1
0
出力例3
4
3
2
1
0
入力例4
10
31415926
a b c d
3
4
5
6
7
8
9
ZZZZZZZZZ
出力例4
31415926
a b c d
3
4
5
6
7
8
9
ZZZZZZZZZ
解答例:
# [解答例1] 受け取ったデータをそのまま表示 # 繰り返し回数 n を受けとり整数型に変換 n = gets.to_i # 続く n 行分のデータを受け取って表示する n.times do puts gets.chomp end # [解答例2] データを一旦配列に代入する # 繰り返し回数 n を受けとり整数型に変換 n = gets.to_i # 配列を初期化 input_ary = [] # 続く n 行分のデータを配列末尾から挿入する n.times do input_ary.push(gets.chomp) end # input_ary の先頭から順番に表示 puts input_ary
1行目に半角スペース区切りで 高さh, 幅w が与えられ、続くh行に半角区切りw個データがある場合(二次元配列)
標準入力 |
3 4 |
a b c d |
e f g h |
i j k l |
input_line = gets.split h = input_line[0].to_i w = input_line[1].to_i # このプログラムでは省略してもOK input_ary = [] # h 回の繰り返し処理 h.times do input_ary.push(gets.split) end p input_ary # > [["a", "b", "c", "d"],["e", "f", "g", "h"], ["i", "j", "k", "l"]]
times・splitメソッドを組み合わせてpaiza練習問題を解いてみる
N 行 M 列の整数の入力 (paizaランク D 相当)
※ paiza レベルアップ問題集 標準入力サンプル問題セット より
問題:
1 行目で整数 N と整数 M が与えられます。
2 行目以降で N 行 M 列の行列が与えられます。上から i 番目、左から j 番目の整数は a_{i,j} です。
N 行 M 列の行列をそのまま出力してください。
入力される値
1 行目で整数 N と整数 M が与えられます。
2 行目以降で N 行 M 列の行列が与えられます。
以下の形式で標準入力によって与えられます。
N M
a_{1,1} ... a_{1,M}
... ...
a_{N,1} ... a_{N,M}
期待する出力
N 行 M 列の行列をそのまま出力してください。
また、末尾に改行を入れ、余計な文字、空行を含んではいけません。
a_{1,1} ... a_{1,M}
... ...
a_{N,1} ... a_{N,M}
条件
すべてのテストケースにおいて、以下の条件をみたします。
- N, M は 1 以上 100 以下の整数
- a_{i,j} は 1 以上 100 以下の整数 (1 ≦ i ≦ N, 1 ≦ j ≦ M)
入力例1
2 3
1 2 3
8 1 3
出力例1
1 2 3
8 1 3
# [解答例1] そのまま表示 input_line = gets.chomp.split n = input_line[0].to_i m = input_line[1].to_i # n 行分のデータを表示 n.times do puts gets end # [解答例2] 一旦配列に格納してから表示 input_line = gets.split n = input_line[0].to_i m = input_line[1].to_i # 配列を初期化 input_ary = [] n.times do # 半角スペースで分割した配列を input_ary 配列の末尾に挿入 input_ary.push(gets.split) end # input_ary の中身は # p input_ary # > [["1", "2", "3"], ["8", "1", "3"]] # 各行の要素を join メソッドで半角スペースで結合して表示 input_ary.each do |line| puts line.join(" ") end
今回のまとめ
paizaで出題される標準入力パターンを一通り紹介しました。
今回は紹介しませんでしたが、配列の中身を全て整数型にしたい場合があります。その場合はmapメソッドを使うと簡単に変換することが出来ますので、次回以降の記事で紹介していきたいと思います。