Ruby プログラミング

[Ruby] 標準入力からのデータ取得1

stdin_1

こんにちは!じゃいごテックのあつしです。

paizaAOJなどのプログラミング学習サイトでプログラミングのテストが受けられるのですが、問題を解く以前に標準入力でのデータ受け取りが難しいという話をよく聞きます。
自分も最初のころはかなり苦労したのでまとめてみます。

標準入力とは

実行されているプログラムがデータを受け取る入力元のことです。キーボードやファイルからのデータ入力と思っておけば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メソッドを使うと簡単に変換することが出来ますので、次回以降の記事で紹介していきたいと思います。

【PR】Ruby学習でお世話になった本




【PR】アルゴリズム学習でお世話になった本


アルゴリズム関連の技術書は大抵C/C++で書かれていますが、ある程度プログラムが出来れば、処理の流れは理解することが出来ます。






通称: 螺旋本。C++で解説されています。AOJ(Aizu Online Judge)を運営している会津大学の渡辺准教授が書いた本です。データ構造や計算量の内容から丁寧に書いてありますのでアルゴリズム学習の最初の参考書としてオススメです。







通称: 蟻本。C++で解説されています。競技プログラミング中級者の定番書と言われていて、競技プログラミングで利用できる色々なアルゴリズムを学ぶことが出来ます。かなり高度な内容も含まれているので1冊分を完全に理解するのにはかなりの時間がかかりそうですが、手元に置いて何度も読み返しています。







通称: チーター本。C#, C++, JAVAでTopcoderの問題を解説しています。初心者~中級者向けに書かれているので解説が非常に丁寧です。







Python3でアルゴリズムを解説している本です。講義スタイルの本で、図やフローチャートを使ってアルゴリズムとデータ構造についてしっかりと説明されています。代わりにコードは少なめです。Pythonコードが読めなくても十分理解できると思います。


-Ruby, プログラミング
-,

© 2024 じゃいごテック