01. スネークケースとキャメルケースを変換する
user_id と userId を行き来させます。データベースの列名と JSON のキー、定数と変数のように、名前の書き方をそろえるときに使います。HTTPServer のように大文字が続く名前で、よく見かける書き方はほとんどの言語で崩れます。 ほかの言語・正解の例・解説 →
CaseConvert.java21.0.12 で 14 / 14 件の出力が正解と一致
// スネークケース(user_id)とキャメルケース(userId)を、指定した向きに変換する
import java.util.Locale;
public class CaseConvert {
public static void main(String[] args) {
String mode = args[0], s = args[1];
// matches は文字列全体が合うかを見る(find は一部が合えば通ってしまう)
if (!s.matches("[A-Za-z0-9_]*")) {
System.out.println("NG: 半角の英数字と _ だけにしてください");
return;
}
if (mode.equals("snake")) {
// 切れ目は2種類。小文字か数字の次の大文字(userId)と、大文字の続きの最後で次が小文字のもの(HTTP|Server)
String out = s.replaceAll("(?<=[a-z0-9])(?=[A-Z])", "_")
.replaceAll("(?<=[A-Z])(?=[A-Z][a-z])", "_");
// toLowerCase() は動かす場所の言語の設定を使う。トルコ語だと I が「ı」になるので Locale.ROOT を渡す
System.out.println(out.toLowerCase(Locale.ROOT));
return;
}
StringBuilder sb = new StringBuilder();
// split("_") は「__」や先頭の「_」で空の部分を作る。空の部分に substring(0, 1) をすると落ちるので飛ばす
for (String p : s.split("_")) {
if (p.isEmpty()) continue;
if (sb.length() == 0) {
sb.append(p.toLowerCase(Locale.ROOT));
} else {
sb.append(p.substring(0, 1).toUpperCase(Locale.ROOT)).append(p.substring(1).toLowerCase(Locale.ROOT));
}
}
System.out.println(sb);
}
}
02. HTML の特殊文字をエスケープする
利用者が入力した文字を HTML に埋め込む前に、< > & " ' を < などに置き換えます。標準の関数は言語ごとに ' の書き方が違い、この決まりの形とそのまま一致したのは Ruby だけでした。 ほかの言語・正解の例・解説 →
Escape.java21.0.12 で 8 / 8 件の出力が正解と一致
// 受け取った文字列の & < > " ' を、HTML の書き方(& < > " ')に変える
public class Escape {
public static void main(String[] args) {
String s = args[0];
// Java の標準にはエスケープの関数がないので、1文字ずつ見て置き換える。
// 5文字はどれも1つの char に収まるので、絵文字(char 2つ)はそのまま書き写される
StringBuilder out = new StringBuilder();
for (char c : s.toCharArray()) {
switch (c) {
case '&' -> out.append("&");
case '<' -> out.append("<");
case '>' -> out.append(">");
case '"' -> out.append(""");
case '\'' -> out.append("'");
default -> out.append(c);
}
}
System.out.println(out);
}
}
03. 順番を保って重複を消す
同じ値の2回目以降を消し、初めて出た順に残します。タグの一覧や、選んだ項目の一覧でよく使います。集合(Set)に入れて戻す書き方では、言語によって順番が変わったり、実行するたびに変わったりします。 ほかの言語・正解の例・解説 →
Unique.java21.0.12 で 9 / 9 件の出力が正解と一致
// 受け取った文字列から、2回目以降に出たものを消し、初めて出た順に出す
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.Set;
public class Unique {
public static void main(String[] args) {
// HashSet は並びがハッシュ値で決まる。入れた順を覚えるのは LinkedHashSet。
// String の equals は char を1つずつ比べるので、「が」1文字と「か」+濁点は別になる
Set<String> out = new LinkedHashSet<>(Arrays.asList(args));
for (String s : out) {
System.out.println(s.isEmpty() ? "(空)" : s);
}
System.out.println(out.size() + "件(" + (args.length - out.size()) + "件を消した)");
}
}
04. 日付と時刻を ISO 8601 で読み書きする
API やログでよく使う 2026-10-30T09:00:00+09:00 の形を読み、UTC と日本時間で書き直します。標準のパーサーにそのまま渡すと、2月30日を3月2日として読んだり、タイムゾーンの無い日時を動かす場所の時間帯で読んだりします。 ほかの言語・正解の例・解説 →
Iso8601.java21.0.12 で 15 / 15 件の出力が正解と一致
// ISO 8601 の日時を受け取って、UTC と日本時間で書き出す
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.regex.Pattern;
public class Iso8601 {
// 標準の ISO の書式は大文字小文字を区別せず(「t」「z」も通る)、「+09:00:00」も読む。形は先に正規表現で決める
static final Pattern ISO = Pattern.compile("[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(\\.[0-9]{1,9})?(Z|[+-][0-9]{2}:[0-9]{2})?");
public static void main(String[] args) {
var m = ISO.matcher(args[0]);
if (!m.matches()) {
System.out.println("NG: 日時の形ではありません");
return;
}
try {
if (m.group(2) == null) {
// タイムゾーンが無くても、2月30日なら「形が違う」を返したいので、日時としては読んでみる
LocalDateTime.parse(args[0]);
System.out.println("NG: タイムゾーン(Z か +09:00 など)を付けてください");
return;
}
// ISO の書式は STRICT で読むので、2月30日・25時はエラーになる(自分で作る ofPattern は既定が SMART で、2月30日を2月28日にする)
OffsetDateTime t = OffsetDateTime.parse(args[0]);
// ZonedDateTime や ZoneId.systemDefault() を使わず、時差だけ(ZoneOffset)で変える
var utc = t.withOffsetSameInstant(ZoneOffset.UTC);
var jst = t.withOffsetSameInstant(ZoneOffset.ofHours(9));
// 年は yyyy(紀元の年)ではなく uuuu で書く。秒の小数は書式に入れないので切り捨てになる
System.out.println("UTC " + utc.format(DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss'Z'")));
System.out.println("日本時間 " + jst.format(DateTimeFormatter.ofPattern("uuuu年MM月dd日 HH:mm:ss")));
} catch (DateTimeParseException e) {
System.out.println("NG: 日時の形ではありません");
}
}
}
05. アクセスログを集計する
Web サーバーのアクセスログを標準入力から読み、ステータスごとの件数と、応答の遅いリクエストの上位3件を出します。空白で区切って何番目かを取る書き方は手軽ですが、壊れた行を数えたり、応答秒を文字のまま並べたりします。 ほかの言語・正解の例・解説 →
AccessLog.java21.0.12 で 8 / 8 件の出力が正解と一致
// 標準入力のアクセスログを読んで、件数・ステータスごとの件数・遅い順の上位3件を出す
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class AccessLog {
// 日時やHTTPの版には空白が入るので、空白で区切って何番目と数えず、行の形そのものを正規表現で確かめる
static final Pattern LOG = Pattern.compile(
"\\S+ \\S+ \\S+ \\[[^\\]]+\\] \"(\\S+) (\\S+) [^\"]+\" ([0-9]{3}) ([0-9]+|-) ([0-9]+\\.[0-9]+)");
record Row(String sec, double secNum, String method, String path, String status) {}
public static void main(String[] args) throws IOException {
List<Row> rows = new ArrayList<>();
int bad = 0;
// readLine は \n でも \r\n でも行を区切り、行の終わりの記号を取って返す
BufferedReader in = new BufferedReader(new InputStreamReader(System.in, StandardCharsets.UTF_8));
String line;
while ((line = in.readLine()) != null) {
if (line.isEmpty()) {
continue;
}
// find は行のどこかに合えば通る。行全体が形どおりか確かめるのは matches
Matcher m = LOG.matcher(line);
if (!m.matches()) {
bad++;
continue;
}
rows.add(new Row(m.group(5), Double.parseDouble(m.group(5)), m.group(1), m.group(2), m.group(3)));
}
System.out.println("件数 " + rows.size());
if (bad > 0) {
System.out.println("読めない行 " + bad);
}
// HashMap は取り出す順番が決まっていない。TreeMap はキーの小さい順に取り出せる
Map<String, Integer> counts = new TreeMap<>();
for (Row r : rows) {
counts.merge(r.status(), 1, Integer::sum);
}
System.out.println("ステータス");
for (Map.Entry<String, Integer> e : counts.entrySet()) {
System.out.println(e.getKey() + " " + e.getValue());
}
System.out.println("遅い順");
// List.sort は安定(同じ秒なら先に出た行が先)。応答秒は文字ではなく double で比べる
rows.sort(Comparator.comparingDouble(Row::secNum).reversed());
for (Row r : rows.subList(0, Math.min(3, rows.size()))) {
System.out.println(r.sec() + " " + r.method() + " " + r.path());
}
}
}
06. URL 用の文字列(slug)を作る
記事のタイトルから「hello-world」のような URL の一部を作ります。ブログや商品ページのアドレスを、見て意味が分かる形にするときに使います。よく見かける書き方は、日本語のタイトルをそのまま URL に入れたり、空の slug を黙って返したりします。 ほかの言語・正解の例・解説 →
Slug.java21.0.12 で 14 / 14 件の出力が正解と一致
// 文字列を URL に使える形(slug)に変える
import java.util.Locale;
import java.util.regex.Pattern;
public class Slug {
static final int MAX = 32;
public static void main(String[] args) {
// toLowerCase() は動かす場所の言語の設定を使い、İ は2文字になる。A〜Z だけを変える
String s = Pattern.compile("[A-Z]").matcher(args[0])
.replaceAll(m -> m.group().toLowerCase(Locale.ROOT));
// \w は既定では ASCII の [A-Za-z0-9_]。残す文字を [a-z0-9] と書く
s = s.replaceAll("[^a-z0-9]+", "-").replaceAll("^-+|-+$", "");
if (s.length() > MAX) {
s = s.substring(0, MAX).replaceAll("-+$", "");
}
System.out.println(s.isEmpty() ? "NG: URL に使える文字がありません" : s);
}
}
07. URL エンコードする
検索語や日本語の名前を、URL のクエリの値(`?q=...`)に入れられる形に変えます。よく見かける書き方は、空白を「+」にしたり、`!` や `~` を変えずに残したりして、受け取る側で別の文字列になります。 ほかの言語・正解の例・解説 →
UrlEncode.java21.0.12 で 15 / 15 件の出力が正解と一致
// 文字列を URL のクエリの値に入れられる形(パーセントエンコード)に変える
import java.nio.charset.StandardCharsets;
public class UrlEncode {
static final String KEEP = "-_.~";
public static void main(String[] args) {
// URLEncoder.encode は空白を「+」、「~」を %7E にして「*」を残す(フォームの形で、URL の形ではない)
StringBuilder sb = new StringBuilder();
for (byte b : args[0].getBytes(StandardCharsets.UTF_8)) {
int v = b & 0xFF; // byte は符号付きなので、そのまま使うと負になる
char c = (char) v;
if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || KEEP.indexOf(c) >= 0) {
sb.append(c);
} else {
sb.append(String.format("%%%02X", v));
}
}
System.out.println(sb);
}
}