Java Tips | 文字列処理:プレースホルダ置換

Java Java
スポンサーリンク

プレースホルダ置換は「文字列に“名前付きの穴”をあけておく」技術

プレースホルダ置換は、文字列の中に {name}${id} のような「名前付きの穴」をあけておき、
あとからそこに値を流し込んで完成形の文字列を作るテクニックです。

メール本文の定型文にユーザー名を入れる。
ログメッセージにIDや状態を埋め込む。
設定ファイルの中の ${HOST}${PORT} を実際の値に置き換える。

こういう「ほぼ固定だけど、一部だけ変わる文字列」を扱うときに、
プレースホルダ置換をユーティリティとして持っておくと、
業務コードが一気に読みやすくなります。

ポイントは、プレースホルダの書式、値の渡し方、未解決プレースホルダの扱い、
この三つをきちんと決めることです。


基本形:{name} 形式のプレースホルダを Map で置き換える

まずはシンプルな置換ユーティリティから

一番素直な形は、テンプレート文字列の中に {name} のような印を入れておき、
Map<String, String> で渡された値で順番に置き換えていく方法です。

import java.util.Map;

public final class PlaceholderReplacer {

    private PlaceholderReplacer() {}

    public static String replace(String template, Map<String, String> values) {
        if (template == null) {
            return null;
        }
        if (values == null || values.isEmpty()) {
            return template;
        }

        String result = template;
        for (Map.Entry<String, String> entry : values.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue();
            if (value == null) {
                value = "";
            }
            String placeholder = "{" + key + "}";
            result = result.replace(placeholder, value);
        }
        return result;
    }
}
Java

使い方のイメージはこうです。

String template = "こんにちは {name} さん。今日は {date} です。";

Map<String, String> values = Map.of(
        "name", "山田",
        "date", "2026-02-04"
);

String message = PlaceholderReplacer.replace(template, values);
System.out.println(message);
// → こんにちは 山田 さん。今日は 2026-02-04 です。
Java

ここで押さえておきたい重要ポイントは三つあります。

一つ目は、「テンプレートはただの文字列」であることです。
特別なクラスではなく、String{name} のような印を埋め込んでいるだけなので、
扱い方は普通の文字列と同じです。

二つ目は、「値は Map で渡す」という設計にしていることです。
これにより、プレースホルダ名と値の対応がはっきりし、
順番に依存しない書き方ができます。
{name}{date} の順番を変えても、Map のキーで正しく置換されます。

三つ目は、「null の値は空文字にする」というポリシーを決めていることです。
ここをどうするかで挙動が変わります。
「null のときはそのまま {name} を残す」「例外にする」なども選択肢ですが、
まずはシンプルに「空文字にする」から始めると扱いやすいです。


未解決プレースホルダをどう扱うかを決める

置き換えられなかった {xxx} を残すか、エラーにするか

先ほどの実装では、Map に存在するキーだけを置き換えています。
テンプレートに {age} があっても、Map に "age" がなければ {age} のまま残ります。

String template = "名前: {name}, 年齢: {age}";
Map<String, String> values = Map.of("name", "山田");

String result = PlaceholderReplacer.replace(template, values);
System.out.println(result);
// → 名前: 山田, 年齢: {age}
Java

これをどう扱うかは要件次第です。

「未解決のプレースホルダが残っていたらバグなので、例外にしたい」
「未解決のものは空文字にしてしまいたい」
「未解決のものは "-" などのデフォルト値にしたい」

こういったルールをユーティリティ側で決めておくと、
業務コードがスッキリします。

例えば、「未解決プレースホルダがあったら例外にする」版はこう書けます。

public static String replaceStrict(String template, Map<String, String> values) {
    String result = replace(template, values);
    int open = result.indexOf('{');
    while (open != -1) {
        int close = result.indexOf('}', open);
        if (close != -1) {
            String unresolved = result.substring(open, close + 1);
            throw new IllegalStateException("Unresolved placeholder: " + unresolved);
        }
        open = result.indexOf('{', open + 1);
    }
    return result;
}
Java

ここでの重要ポイントは、「プレースホルダ置換の“失敗”をどう検知するか」を決めていることです。
その場しのぎで replace を書くのではなく、
「未解決があったらエラー」というルールをユーティリティに閉じ込めることで、
バグを早く見つけられるようになります。


正規表現でプレースホルダの形をきちんと定義する

{name} だけを対象にし、似た文字列に誤爆しないようにする

単純な replace("{" + key + "}", value) は簡単ですが、
場合によっては「意図しない部分まで置き換えてしまう」ことがあります。

より厳密にやるなら、
{} に囲まれた英数字+アンダースコアだけをプレースホルダとみなす」
といったルールを正規表現で定義します。

import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public final class RegexPlaceholderReplacer {

    private static final Pattern PLACEHOLDER =
            Pattern.compile("\\{([a-zA-Z0-9_]+)\\}");

    private RegexPlaceholderReplacer() {}

    public static String replace(String template, Map<String, String> values) {
        if (template == null) {
            return null;
        }
        if (values == null || values.isEmpty()) {
            return template;
        }

        Matcher matcher = PLACEHOLDER.matcher(template);
        StringBuffer sb = new StringBuffer();
        while (matcher.find()) {
            String key = matcher.group(1);
            String value = values.get(key);
            if (value == null) {
                value = "";
            }
            matcher.appendReplacement(sb, Matcher.quoteReplacement(value));
        }
        matcher.appendTail(sb);
        return sb.toString();
    }
}
Java

使い方は先ほどと同じです。

String template = "ID={id}, STATUS={status}";
Map<String, String> values = Map.of(
        "id", "JOB-001",
        "status", "SUCCESS"
);

String result = RegexPlaceholderReplacer.replace(template, values);
System.out.println(result);
// → ID=JOB-001, STATUS=SUCCESS
Java

ここで深掘りしたいポイントは二つです。

一つ目は、「プレースホルダの“形”を正規表現で定義している」ことです。
{abc} はOKだが {a-b} はNG、など、ルールをはっきりさせられます。
これにより、「たまたま {} が出てきただけの部分」を誤って置換するリスクを減らせます。

二つ目は、「Matcher.quoteReplacement を使って、値の中に $\ があっても安全に置き換えている」ことです。
正規表現ベースの置換では、値の中身が正規表現のメタ文字として解釈されないようにするのが重要です。


実務でのプレースホルダ置換の使いどころ

メール本文や通知メッセージの整形

例えば、こんなテンプレートを用意しておきます。

String template = """
{userName} 様

いつもご利用ありがとうございます。
{date} 時点のご利用状況は以下の通りです。

・プラン: {plan}
・利用回数: {count} 回

今後ともよろしくお願いいたします。
""";
Java

サーバ側では、次のように置換します。

Map<String, String> values = Map.of(
        "userName", "山田太郎",
        "date", "2026-02-04",
        "plan", "プレミアム",
        "count", "12"
);

String body = RegexPlaceholderReplacer.replace(template, values);
System.out.println(body);
Java

こうしておくと、「文章(テンプレート)」と「値(ロジック)」をきれいに分離できます。
文章を変更したいときはテンプレートだけを直せばよく、
コード側のロジックには手を入れなくて済みます。

ログメッセージやエラーメッセージのフォーマット

ログやエラーメッセージでも、プレースホルダ置換はよく使えます。

String template = "処理ID={id}, 状態={status}, 所要時間={time}ms";

Map<String, String> values = Map.of(
        "id", "JOB-20260204-001",
        "status", "SUCCESS",
        "time", "1234"
);

String logMessage = PlaceholderReplacer.replace(template, values);
System.out.println(logMessage);
// → 処理ID=JOB-20260204-001, 状態=SUCCESS, 所要時間=1234ms
Java

このように、「フォーマットは固定」「値だけ変わる」という場面では、
プレースホルダ置換がとても相性が良いです。


まとめ:プレースホルダ置換ユーティリティで身につけたい感覚

プレースホルダ置換は、
「文字列に名前付きの穴をあけておき、あとから値を流し込む」ための技術です。

大事なのは、

プレースホルダの書式({name} など)を決めること。
値は Map で渡し、名前で紐づけること。
未解決プレースホルダをどう扱うか(残す・空文字・エラー)を決めること。
必要に応じて正規表現でプレースホルダの形を厳密に定義すること。

もしあなたのコードのどこかに、

String msg = "こんにちは " + name + " さん。今日は " + date + " です。";
Java

のような「文字列連結だらけ」の行があったら、
それをプレースホルダ置換に置き換えてみてください。

その小さな一歩が、
「文章とロジックをきれいに分離できるエンジニア」への、
確かなステップになります。

タイトルとURLをコピーしました