String.format で書式化

生田 陸人
LuaGate エンジニア / 現役エンジニア
編集 LuaGate編集部

このレッスンで分かること

  • String.format("%s は %d 歳", name, age) で書式化された文字列を生成
  • %s 文字列、%d 整数、%f 小数、%n 改行 (\n よりプラットフォーム別で安全)
  • %05d で 0 埋め幅指定、%.2f で小数 2 桁
  • Java 15+ なら "...".formatted(args) でメソッドチェーンに書ける

String.format で書式化 とは

JavaString.format%s %d %,d %.2f などの書式指定子を使い、整形された文字列を組み立てる方法を学ぼう。

String.format で「整った文字列」を作る

前章までで + 演算子による文字列連結はもう自由に書けるようになりました。ただ、いざ業務で領収書や帳票、ログを整える段になると、+ 連結だけでは少し物足りなく感じる場面が出てきます。たとえば「金額にカンマ区切りを入れたい」「小数点以下を 2 桁に揃えたい」「右詰めで桁を合わせたい」といった要求です。"¥" + price だけでは、120000 がそのまま "¥120000" と出てしまい、人間にとっては読みにくいですよね。

Java

String s1 = "¥" + 120000; // "¥120000" — 読みづらい String s2 = String.format("¥%,d", 120000); // "¥120,000" — カンマ区切りで読みやすい

このレッスンでは、String.format を使って「書式付きの文字列」を組み立てる方法を学んでいきます。JavaString.format は、C 言語printf を知っている人なら一発でピンとくる、テンプレートに値を流し込む書き方です。%s の場所には文字列が、%d の場所には整数が、%.2f の場所には小数点以下 2 桁の小数が、というふうに、書式指定子と引数が順番に対応します。

String.format は「整形された 1 行を返す関数」、System.out.printf は「整形した結果をその場で標準出力に流す関数」です。書式指定子の文法は同じですが、用途が違うので使い分けに気をつけてください。

業務アプリの世界では、ログ行の桁を揃えたり、領収書の金額にカンマを入れたり、グラフのラベルで小数を丸めたり、と書式化が必要になる場面は数えきれません。Android アプリでも Windows のデスクトップアプリでも、画面に値を出すときの「最後のひと整え」を担うのが String.format だと考えてください。

基本の文法は String.format(テンプレート, 引数...)

String.format の使い方はシンプルです。第 1 引数に %s%d などの書式指定子を埋めたテンプレート文字列を渡し、第 2 引数以降に対応する値を順番に渡します。

Java

String s = String.format("%s さんは %d 歳です", "田中", 25); // → "田中 さんは 25 歳です"

%s には "田中"%d には 25 が順番に流し込まれます。書式指定子と引数の数は必ず一致させましょう。数が合わないと、MissingFormatArgumentException という実行時例外が飛んできます。

diagram (will load when visible)

この「テンプレート + 引数」の構造は、Python"{} さん".format(...)JavaScript のテンプレートリテラル \${name} さん` と発想が同じです。Java ではテンプレートリテラルがない代わりに、String.format` が長年その役目を担ってきた、と覚えておくと頭の整理がしやすいです。

主要な書式指定子を覚えよう

書式指定子はたくさんありますが、まずは次の 5 つを押さえれば実務の 9 割はカバーできます。

  • %s — 文字列を埋め込む。String でも Object でも、toString() の結果が入ります
  • %dint long などの整数を埋め込む。10 進数表記
  • %fdouble float の小数を埋め込む。デフォルトは小数点以下 6 桁
  • %.2f — 小数点以下を 2 桁に丸めて埋め込む。%.3f なら 3 桁
  • %,d — 3 桁ごとにカンマ区切りを入れた整数を埋め込む。%,.2f のように小数にも使える

コードで見てみましょう。

Java

String a = String.format("%s: %d", "apple", 100); // "apple: 100" String b = String.format("%s: ¥%,d", "ノートPC", 120000); // "ノートPC: ¥120,000" String c = String.format("円周率は %.2f", 3.14159); // "円周率は 3.14" String d = String.format("合計 %,d 件 / 平均 %.1f", 12345, 87.5); // → "合計 12,345 件 / 平均 87.5"

%,d がカンマ区切りを入れてくれるおかげで、120000120,000 に整形されます。これは経理系の画面や領収書で頻繁に登場する書式で、+ 連結だと自分で書くのは結構面倒な処理を、String.format だと 1 文字加えるだけで実現できる、というのが嬉しいところです。

書式指定子は % のあとに「フラグ → 幅 → 精度 → 型」の順で記述します。たとえば %-10.2f なら「左詰め、幅 10、小数点以下 2 桁、double 型」という意味です。最初は %s %d %,d %.2f の 4 つを覚えれば十分で、必要になったときに公式 Javadoc の Formatter を見ればいくらでも応用が利きます。

printfformat の違いは「出力 vs 文字列生成」

初心者がよく混乱するのが、System.out.printfString.format の役割の違いです。書式指定子の文法はまったく同じなのに、結果の扱いがまるで違います。

Java

String name = "田中"; int age = 25; // printf: 標準出力に直接書き出す。戻り値は PrintStream で普通は使わない System.out.printf("%s さんは %d 歳です%n", name, age); // format: 文字列を組み立てて返すだけ。出力はしない String line = String.format("%s さんは %d 歳です", name, age); System.out.println(line);

printf は「整形しながらその場で System.out に書き出す」関数で、戻り値は基本的に捨てます。format は「整形した文字列を返す」関数で、戻り値をログに渡したり、ファイルに書き出したり、別の文字列とさらに連結したりと自由に使えます。

テストで戻り値を比較する Java executor のような環境では printf は使えません (標準出力を捕まえない)。文字列を作って return したいときは必ず String.format を使う、と覚えてください。

diagram (will load when visible)

Locale を意識すると多言語対応で安心

もう一歩踏み込むと、String.format は実は「ロケール (Locale) に依存する」関数です。ロケールというのは「どの国・どの言語の流儀で書式化するか」の設定で、たとえば Locale.JAPAN ではカンマが千の位区切り、Locale.GERMANY ではピリオドが千の位区切りでカンマが小数点、というふうに国ごとに違います。

Java

String jp = String.format(java.util.Locale.JAPAN, "%,.2f", 1234567.89); // → "1,234,567.89" String de = String.format(java.util.Locale.GERMANY, "%,.2f", 1234567.89); // → "1.234.567,89" ← ピリオドとカンマの役割が逆!

String.format("%,.2f", 1234567.89) のようにロケールを省略すると、実行環境のデフォルトロケールが使われます。日本のサーバーで動かしているなら大体 Locale.JAPAN ですが、海外のクラウドだと違う結果になることがあります。国際対応のサービスで String.format を使うときは、必ず Locale を明示するのがプロの作法です。

ロケールを意識しないで書いたバッチ処理が、CI 環境では Locale.US で動いてしまい、本番と数値の見え方が違って気付くまで数日かかった……というのは Java の現場でよくある事故です。「カンマ区切り」と「小数点」は地域で揺れる、と覚えておきましょう。

今回の課題では英数字と日本語の組み合わせなのでロケールは特に指定しませんが、本物のシステムでは Locale.JAPAN を渡しておくとあとから困りません。

よくある間違い

String.format まわりで初心者が必ずハマる落とし穴を 3 つ挙げます。

  • %dString を渡してしまうString.format("%d", "100") のように、文字列を %d の位置に入れると、IllegalFormatConversionException という実行時例外が飛びます。整数なら %d、文字列なら %s、と型を必ず合わせましょう。逆に %s には int でも double でも何でも入れられる (自動で toString() される) ので、迷ったときの保険として %s は便利です
  • \n%n を取り違える — 改行を入れたいとき、\n は環境にかかわらず常に LF (0x0A) になりますが、%n は OS ごとの改行 (Windows なら CRLF、Linux/macOS なら LF) に展開されます。ログや帳票を Windows でも Linux でも揃えたいときは %n を、HTML などプラットフォーム非依存で必ず LF にしたいときは \n を、というふうに使い分けます
  • 桁あふれと意図しない丸め%.2f の丸めは HALF_UP(いわゆる四捨五入)です。ただし double/float0.10.2 などを二進浮動小数点で正確に表現できないため、表示する前の値そのものに微小な誤差が乗ります。お金の計算など厳密な精度が必要なときは BigDecimalsetScale(2, RoundingMode.HALF_UP) を使うのが安全です。String.format%.2f はあくまで「画面表示用の見た目を整える」ためのもの、と割り切りましょう

もう 1 つよくあるのが、引数の数を間違えてしまうパターンです。String.format("%s, %s, %s", "a", "b") のように指定子より引数が少ないと MissingFormatArgumentException、逆に多すぎる場合はエラーにはなりませんが余った引数が無視されます。テンプレートと引数の対応は、書いたあと一度目で追って確認するのが鉄則です。

IDE の警告機能を有効にしておくと、書式指定子の型不一致や引数の数の食い違いを保存時に検知してくれます。IntelliJ IDEAVS Code の Java 拡張なら標準で対応しているので、警告は赤いまま放置せず、必ず潰すクセを付けてください。

実務で遭遇するパターン

中級レッスンで学んだことが、実際の業務コードでどう登場するかを整理します。

  • ログメッセージ整形logger.info("User {} login at {}", id, timestamp) のような構造化ログ
  • CSV / TSV 出力String.format("%s,%s,%d", name, dept, age) で行を組み立てて出力
  • 通貨表示String.format("%,d 円", price) で 3 桁区切りカンマ付きの金額表示
  • ファイル名の生成String.format("report-%tY%<tm%<td.csv", date) で日付付きファイル名

コードレビューで指摘されがちなポイント

PR を出すとシニアから入りやすい指摘です。先回りで身につけておけばレビューが一発で通ります。

  • + 連結で済むのに format ─ 静的な 1 〜 2 引数の連結は + のほうが読みやすいと言われる
  • ロケール無視%f はロケール依存で日本では 3,14 (小数点がカンマ) になる。明示的に Locale.ROOT を渡す
  • フォーマット文字列を i18n していない ─ メッセージは MessageFormatResourceBundle を使うのが本筋
  • %n ではなく \n を使っている ─ Windows / Unix の改行差で問題になることがあり、%n が安全

パフォーマンス考慮事項

  • String.format は正規表現マッチ込みで重い ─ ホットパスでは StringBuilder のほうが 2〜10 倍速い
  • String.valueOf でいいのに format を呼んでいる ─ 単一引数なら String.valueOf(n)n + "" のほうが軽い
  • ログでも Slf4j{} 記法のほうが軽い ─ レベルが OFF のときに format を実行しないため
  • フォーマット文字列の事前検査 ─ Java はランタイムまで型チェックしない。テストで全パターンを通すべき
この章のポイント

ここまでの要点 String.format は強力だが正規表現込みで重い。簡単な連結は +、ログは Slf4j{} 記法。Locale 指定を忘れない。

やってみよう

それでは今回の課題に挑戦してみましょう。今回作るのは priceLabel(String name, int price) で、商品名と整数の価格を受け取り、"商品名: ¥価格" という形式の文字列を返します。価格部分には 千の位カンマ区切り を入れる必要があるので、String.format%,d が活躍します。

手順は次のとおりです。

  1. メソッド本体で String.format("%s: ¥%,d", name, price) を呼ぶ
  2. その戻り値をそのまま return する
  3. テストで priceLabel("リンゴ", 100)"リンゴ: ¥100"priceLabel("ノートPC", 120000)"ノートPC: ¥120,000" になることを確認する

書式指定子の : の後ろには半角スペースを 1 つ、¥ のあとにはスペースなし、で組み立てます。%,d%d にしてしまうと 120000 がカンマなしの 120000 のままになってテストが fail するので、カンマフラグを忘れずに付けてください。

慣れてきたら、自分で "%s: ¥%,.2f" を使って double の価格を扱ってみたり、Locale.JAPAN を明示してみたり、%-10s のような左詰めフォーマットを試したりと、いろいろ実験してみるのがおすすめです。String.format を自由に扱えるようになると、ログ整形・レポート生成・テンプレート出力など、業務で書くコードの「最後のひと整え」がぐっと楽になります。

次のレッスンでは、ここで学んだ書式化を組み合わせて、もう少し複雑なテキスト整形に踏み込んでいきます。

よくある質問

Q. 中級の内容は実務でどれくらい使いますか?

A. Collection(List/Map/Set)、Stream、例外処理、ジェネリクスは毎日のように登場します。Date/Time API、Files、try-with-resources も実務で頻出するため、本コースの内容は実プロジェクトでそのまま役立ちます。

Q. Stream と for ループはどっちで書くべき?

A. 可読性で選んでください。filter + map + collect が綺麗にハマるなら Stream、副作用や複雑な分岐が多いなら for ループの方が読みやすいです。一律にどちらを使うべきという正解はなく、チームのコーディング規約に合わせるのが現実的です。

Q. 次のステップでは何を学ぶべきですか?

A. 中級の基礎が固まったら、Spring Boot で Web API を作る、JUnit でテストを書く、Maven/Gradle でビルドを管理する、といった実プロジェクトのスキルに進むと効果的です。OSS のコードを読む経験も大きく成長を促します。

次のレッスン

次は StringBuilder で連結 で、StringBuilder で連結 を学びます。

事前確認 — 進む前に次の 3 つができることを確認しましょう。

  1. String.format の要点を自分の言葉で説明できる
  2. このレッスンの最小コード (または操作手順) を見ずに書ける
  3. 練習問題やクイズで間違えた箇所を読み直して理解した

理解度チェック (30 秒)

Q. String.format とは何か、1 文で説明してください。

この章のポイント

A. 本文の「このレッスンで分かること」または冒頭の説明文を見直し、自分の言葉で要約できれば OK。詰まったら本レッスンの最初の H2 セクションを読み返してみましょう。

関連レッスン

要件

  1. クラス名は Solution、メソッド名は priceLabel、引数は String name, int price の 2 つ、戻り値の型は String にすること
  2. 本文中で示した String.format("%s: ¥%,d", name, price) の書式指定子を使って整形すること (+ 連結や StringBuilder は使わない)
  3. %,d のカンマフラグを必ず付け、120000"120,000" のように千の位区切りで表示されるようにすること

入出力例

test-cases.txt

priceLabel("リンゴ", 100)"リンゴ: ¥100" priceLabel("ノートPC", 120000)"ノートPC: ¥120,000" priceLabel("", 0)": ¥0" priceLabel("コーヒー", 1234)"コーヒー: ¥1,234" priceLabel("冷蔵庫", 1500000)"冷蔵庫: ¥1,500,000"

ヒント

main.java
main.java
学習モード

メモ

String.format で書式化

⌘S で保存