01. スネークケースとキャメルケースを変換する
user_id と userId を行き来させます。データベースの列名と JSON のキー、定数と変数のように、名前の書き方をそろえるときに使います。HTTPServer のように大文字が続く名前で、よく見かける書き方はほとんどの言語で崩れます。 ほかの言語・正解の例・解説 →
case_convert.rb4.0.7 で 14 / 14 件の出力が正解と一致
# スネークケース(user_id)とキャメルケース(userId)を、指定した向きに変換する
NG = "NG: 半角の英数字と _ だけにしてください"
mode, s = ARGV
# Ruby の ^ $ は「行」の頭と終わり。文字列全体を見るときは \A と \z を使う(改行のあとを素通りさせない)
if s !~ /\A[A-Za-z0-9_]*\z/
puts NG
elsif mode == "snake"
# 切れ目は2種類。小文字か数字の次の大文字(userId)と、大文字の続きの最後で次が小文字のもの(HTTP|Server)
puts s.gsub(/(?<=[a-z0-9])(?=[A-Z])/, "_").gsub(/(?<=[A-Z])(?=[A-Z][a-z])/, "_").downcase
else
# split("_") は「__」のところで空の部分を作る(先頭の空も残る)。reject で捨てる
parts = s.split("_").reject(&:empty?)
# capitalize は先頭を大文字・残りを小文字にする。空の配列なら join は ""
puts parts.each_with_index.map { |p, i| i.zero? ? p.downcase : p.capitalize }.join
end
02. HTML の特殊文字をエスケープする
利用者が入力した文字を HTML に埋め込む前に、< > & " ' を < などに置き換えます。標準の関数は言語ごとに ' の書き方が違い、この決まりの形とそのまま一致したのは Ruby だけでした。 ほかの言語・正解の例・解説 →
escape.rb4.0.7 で 8 / 8 件の出力が正解と一致
# 受け取った文字列の & < > " ' を、HTML の書き方(& < > " ')に変える # CGI.escapeHTML は5文字を & < > " ' に変える(この回の決まりと同じ)ので、そのまま使う。 # Ruby 4.0 では cgi ライブラリの大半が外されたが、エスケープの部分(cgi/escape)は残っている require "cgi/escape" puts CGI.escapeHTML(ARGV[0])
03. 順番を保って重複を消す
同じ値の2回目以降を消し、初めて出た順に残します。タグの一覧や、選んだ項目の一覧でよく使います。集合(Set)に入れて戻す書き方では、言語によって順番が変わったり、実行するたびに変わったりします。 ほかの言語・正解の例・解説 →
unique.rb4.0.7 で 9 / 9 件の出力が正解と一致
# 受け取った文字列から、2回目以降に出たものを消し、初めて出た順に出す
items = ARGV
# uniq は初めて出た順を保つ。比べるのは文字の並び(バイト)なので、「が」1文字と「か」+濁点は別になる
out = items.uniq
out.each { |s| puts(s.empty? ? "(空)" : s) }
puts "#{out.size}件(#{items.size - out.size}件を消した)"
04. 日付と時刻を ISO 8601 で読み書きする
API やログでよく使う 2026-10-30T09:00:00+09:00 の形を読み、UTC と日本時間で書き直します。標準のパーサーにそのまま渡すと、2月30日を3月2日として読んだり、タイムゾーンの無い日時を動かす場所の時間帯で読んだりします。 ほかの言語・正解の例・解説 →
iso8601.rb4.0.7 で 15 / 15 件の出力が正解と一致
# ISO 8601 の日時を受け取って、UTC と日本時間で書き出す
require "date"
# Time.iso8601 は小文字の t・z、「+0900」を受け付け、タイムゾーンが無ければ動かす場所の時間帯で読み、
# 2月30日を3月2日にする。形は正規表現で決める。^ $ は行の頭と終わりなので、文字列の頭と終わりは \A \z
ISO = /\A(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(\.\d{1,9})?(Z|[+-]\d{2}:\d{2})?\z/
def convert(s)
m = ISO.match(s)
return "NG: 日時の形ではありません" unless m
y, mo, d, h, mi, se = m.captures[0, 6].map(&:to_i)
# Time.utc(2026, 2, 30) は黙って3月2日になる。日付は Date.valid_date? で、時刻は範囲で確かめる
return "NG: 日時の形ではありません" unless Date.valid_date?(y, mo, d) && h <= 23 && mi <= 59 && se <= 59
tz = m[8]
return "NG: タイムゾーン(Z か +09:00 など)を付けてください" if tz.nil?
tz = "+00:00" if tz == "Z"
return "NG: 日時の形ではありません" if tz[1, 2].to_i > 23 || tz[4, 2].to_i > 59
# 時差を "+05:30" の形で渡すと、TZ や tzdata を見ずにその時差で作る。getlocal も時差を渡せば同じ
t = Time.new(y, mo, d, h, mi, se, tz)
"UTC #{t.getutc.strftime('%Y-%m-%dT%H:%M:%SZ')}\n日本時間 #{t.getlocal('+09:00').strftime('%Y年%m月%d日 %H:%M:%S')}"
end
puts convert(ARGV[0])
05. アクセスログを集計する
Web サーバーのアクセスログを標準入力から読み、ステータスごとの件数と、応答の遅いリクエストの上位3件を出します。空白で区切って何番目かを取る書き方は手軽ですが、壊れた行を数えたり、応答秒を文字のまま並べたりします。 ほかの言語・正解の例・解説 →
access_log.rb4.0.7 で 8 / 8 件の出力が正解と一致
# 標準入力のアクセスログを読んで、件数・ステータスごとの件数・遅い順の上位3件を出す
# 日時やHTTPの版には空白が入るので、空白で区切って何番目と数えず、行の形そのものを正規表現で確かめる。
# Ruby の ^ と $ は「行の始まり・終わり」で、文字列の中の改行の前後にも合う。文字列全体の始まり・終わりは \A と \z
LOG = /\A\S+ \S+ \S+ \[[^\]]+\] "(\S+) (\S+) [^"]+" ([0-9]{3}) ([0-9]+|-) ([0-9]+\.[0-9]+)\z/
rows = []
bad = 0
# 文字コードを UTF-8 と決めて読む。chomp は行の終わりの \n も \r\n も取る
$stdin.read.force_encoding("UTF-8").each_line do |line|
line = line.chomp
next if line.empty?
m = LOG.match(line)
if m.nil?
bad += 1
next
end
rows << { sec: m[5], method: m[1], path: m[2], status: m[3] }
end
puts "件数 #{rows.length}"
puts "読めない行 #{bad}" if bad > 0
puts "ステータス"
# tally は「値 => 出てきた数」の Hash を作る。Hash は入れた順なので、キーで並べてから出す
rows.map { |r| r[:status] }.tally.sort.each do |status, n|
puts "#{status} #{n}"
end
puts "遅い順"
# sort_by は安定でない(同じ秒の行の順が入れ替わることがある)。何行目かを2つ目のキーにして、先に出た行を先にする
slow = rows.each_with_index.sort_by { |r, i| [-r[:sec].to_f, i] }.first(3)
slow.each do |r, _|
puts "#{r[:sec]} #{r[:method]} #{r[:path]}"
end
06. URL 用の文字列(slug)を作る
記事のタイトルから「hello-world」のような URL の一部を作ります。ブログや商品ページのアドレスを、見て意味が分かる形にするときに使います。よく見かける書き方は、日本語のタイトルをそのまま URL に入れたり、空の slug を黙って返したりします。 ほかの言語・正解の例・解説 →
slug.rb4.0.7 で 14 / 14 件の出力が正解と一致
# 文字列を URL に使える形(slug)に変える
MAX = 32
NG = "NG: URL に使える文字がありません"
# downcase は Unicode の決まりで変えるので、İ が2文字になる。tr で A〜Z だけを変える
s = ARGV[0].tr("A-Z", "a-z")
# Ruby の \w は既定では ASCII だけだが、言語によって違う。残す文字を [a-z0-9] と書く
s = s.gsub(/[^a-z0-9]+/, "-").gsub(/\A-+|-+\z/, "")
s = s[0, MAX].sub(/-+\z/, "") if s.length > MAX
puts s.empty? ? NG : s
07. URL エンコードする
検索語や日本語の名前を、URL のクエリの値(`?q=...`)に入れられる形に変えます。よく見かける書き方は、空白を「+」にしたり、`!` や `~` を変えずに残したりして、受け取る側で別の文字列になります。 ほかの言語・正解の例・解説 →
url_encode.rb4.0.7 で 15 / 15 件の出力が正解と一致
# 文字列を URL のクエリの値に入れられる形(パーセントエンコード)に変える
# CGI.escape は空白を「+」にする。URI.encode_www_form_component はさらに「~」を %7E にして「*」を残す
# .b でバイトの並びにしてから変える(文字のまま gsub すると日本語1文字に %XX が1つになる)
puts ARGV[0].b.gsub(/[^A-Za-z0-9\-_.~]/) { |c| "%%%02X" % c.ord }