Java Tips | 文字列処理:大文字変換

Java Java
スポンサーリンク

大文字変換は「バラバラな表記を“同じもの”として扱う」ための技

業務システムでは、ユーザーや外部システムが好き勝手な表記でデータを送ってきます。
abc, Abc, ABC が混ざっていたり、jp, JP, Jp が混ざっていたり。

人間の感覚では「同じ意味」なのに、プログラム的には別物として扱われてしまう。
これを揃えるための基本テクニックのひとつが 大文字変換(uppercase) です。

検索キー、コード値、国コード、通貨コード、フラグ値などを「全部大文字で扱う」と決めておくと、
比較・検索・ログ出力が一気に安定します。


Java の基本:String#toUpperCase を正しく理解する

まずは一番シンプルな形

Java で大文字変換をする一番基本のメソッドは String#toUpperCase() です。

public class UppercaseBasic {

    public static void main(String[] args) {
        String s = "abcDef";
        String upper = s.toUpperCase();
        System.out.println(upper); // ABCDEF
    }
}
Java

これだけ見ると「簡単じゃん」で終わりそうですが、
業務でちゃんと使うには、もう一歩だけ踏み込んでおきたいポイントがあります。

それが 「ロケール(Locale)を意識する」 という話です。


重要ポイント:toUpperCase は Locale を指定して使う

なぜ Locale を指定する必要があるのか

String#toUpperCase() には、引数なし版と toUpperCase(Locale locale) 版があります。
業務コードでは、基本的に Locale を明示して呼ぶ ことをおすすめします。

import java.util.Locale;

public class UppercaseLocale {

    public static void main(String[] args) {
        String s = "abcDef";

        String upperDefault = s.toUpperCase(); // デフォルトロケール依存
        String upperRoot    = s.toUpperCase(Locale.ROOT); // ロケール非依存

        System.out.println(upperDefault); // 環境依存だが、ほとんどの環境で ABCDEF
        System.out.println(upperRoot);    // 常に ABCDEF
    }
}
Java

なぜかというと、言語によって「大文字・小文字のルール」が微妙に違うからです。
有名なのはトルコ語の i/İ 問題などで、環境のデフォルトロケールに引きずられると、
「ある国のサーバーだけ変換結果が違う」といった事故が起こりえます。

業務で「英字コードを大文字にする」「国コードを大文字にする」といった用途なら、
Locale.ROOT を指定しておくのが一番安全です。

String code = "jp";
String upper = code.toUpperCase(Locale.ROOT); // "JP"
Java

ここは少し地味ですが、「ロケールを明示するクセをつける」ことが、後々のバグを防ぐ大事な一歩です。


ユーティリティ化:業務でよく使う「大文字変換」を名前付きにする

コード値・国コード・通貨コードなどを揃える

業務では、「この値は必ず大文字で扱う」という種類のデータがいくつかあります。
例えば、次のようなものです。

  • 国コード:jp, us, frJP, US, FR
  • 通貨コード:jpy, usdJPY, USD
  • フラグ値:y, nY, N

こういうものを毎回 toUpperCase(Locale.ROOT) と書くのはダルいので、
ユーティリティにまとめてしまいます。

import java.util.Locale;

public final class Uppercases {

    private Uppercases() {}

    public static String upperOrNull(String text) {
        if (text == null) {
            return null;
        }
        return text.toUpperCase(Locale.ROOT);
    }

    public static String upperTrimmedOrNull(String text) {
        if (text == null) {
            return null;
        }
        return text.trim().toUpperCase(Locale.ROOT);
    }
}
Java

使い方はこうです。

String country = "  jp ";
String normalized = Uppercases.upperTrimmedOrNull(country);

System.out.println("\"" + normalized + "\""); // "JP"
Java

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

一つ目は、「null をどう扱うかをユーティリティ側で決めている」ことです。
upperOrNullnull をそのまま返す、upperTrimmedOrNull はトリムしてから大文字にする、
といったルールをメソッド名と実装で固定しておくと、呼び出し側が迷いません。

二つ目は、「トリム+大文字変換という“よくある前処理セット”を一箇所に閉じ込めている」ことです。
検索キーやコード値の正規化では、「前後の空白を削る」「大文字に揃える」がセットで出てくるので、
それをユーティリティとして名前付きにしておくと、コードが一気に読みやすくなります。


例題:検索キーを大文字に揃えて比較する

「大文字・小文字を区別しない検索」を安全に書く

例えば、「商品コードを入力して検索する」画面を考えます。

DB には ABC123 で保存されているけれど、ユーザーは abc123AbC123 と入力してくるかもしれません。
このとき、「大文字・小文字を区別しない検索」をしたいなら、
「両方を同じルールで大文字に揃えてから比較する」のが定番です。

import java.util.Locale;

public final class CodeMatcher {

    private CodeMatcher() {}

    public static boolean equalsIgnoreCaseSafe(String a, String b) {
        if (a == null || b == null) {
            return false;
        }
        return a.toUpperCase(Locale.ROOT)
                .equals(b.toUpperCase(Locale.ROOT));
    }
}
Java

使い方はこうです。

System.out.println(CodeMatcher.equalsIgnoreCaseSafe("abc123", "ABC123")); // true
System.out.println(CodeMatcher.equalsIgnoreCaseSafe("AbC", "aBc"));       // true
System.out.println(CodeMatcher.equalsIgnoreCaseSafe("AbC", null));        // false
Java

もちろん、String#equalsIgnoreCase もありますが、
「ロケールを明示して揃える」「トリムも一緒にやる」など、
プロジェクトのルールをユーティリティ側に閉じ込めたいときには、
こうした自前メソッドを用意しておく価値があります。


例題:ログ出力時にコード値を大文字に統一する

後から grep しやすいログにするための一手間

ログにコード値やフラグ値を出すとき、
"status=ok""status=OK" が混ざっていると、grep しづらくなります。

そこで、「ログに出すときは必ず大文字にする」というルールをユーティリティにしておきます。

import java.util.Locale;

public final class LogCodes {

    private LogCodes() {}

    public static String toLogCode(String code) {
        if (code == null) {
            return "-";
        }
        return code.trim().toUpperCase(Locale.ROOT);
    }
}
Java

使い方はこうです。

String status = " ok ";
System.out.println("status=" + LogCodes.toLogCode(status));
// status=OK
Java

ここでのポイントは、「ログ用の表記ルール(null のときは -、トリムして大文字)を一箇所にまとめている」ことです。
あとから「null のときは UNKNOWN にしたい」「小文字で出したい」といった要件が出ても、
LogCodes だけ直せば、ログ全体の挙動を変えられます。


例題:設定値や環境変数を大文字に揃える

「ON / on / On」を全部同じ意味にする

設定ファイルや環境変数で、ON / on / On などが混ざることがあります。
これも、「大文字に揃えてから判定する」ユーティリティを用意しておくと安全です。

import java.util.Locale;

public final class Flags {

    private Flags() {}

    public static boolean isOn(String value) {
        if (value == null) {
            return false;
        }
        String v = value.trim().toUpperCase(Locale.ROOT);
        return "ON".equals(v) || "TRUE".equals(v) || "1".equals(v);
    }
}
Java

使い方はこうです。

System.out.println(Flags.isOn("on"));    // true
System.out.println(Flags.isOn("ON"));    // true
System.out.println(Flags.isOn(" true ")); // true
System.out.println(Flags.isOn("off"));   // false
Java

ここで深掘りしたいのは、「大文字変換は“判定ロジックの前処理”として使うと威力を発揮する」ということです。
trimtoUpperCase(Locale.ROOT) を一箇所に閉じ込めておくことで、
設定値の解釈がプロジェクト全体で一貫します。


まとめ:大文字変換ユーティリティで身につけたい感覚

大文字変換は、「人間には同じに見えるけれど、プログラム的には違う表記」を揃えるための、超・基本テクニックです。

押さえておきたい感覚は、まず「toUpperCase はロケールに依存するので、業務では toUpperCase(Locale.ROOT) を基本形にする」こと。
次に、「トリムや null ハンドリングと組み合わせた“前処理セット”をユーティリティとして名前付きにし、検索・比較・ログ・設定値などで共通利用する」こと。
そして、「equalsIgnoreCase に頼りきるのではなく、“どう揃えてから比較するか”を自分たちで決めてコードに刻む」ことです。

もしあなたのコードのどこかに、s.toUpperCase()equalsIgnoreCase がバラバラに散らばっているなら、
その一つを題材にして、ここで作った UppercasesCodeMatcherLogCodesFlags のようなユーティリティにまとめてみてください。
それだけで、「読みやすくて、バグりにくくて、仕様変更にも強い文字列処理」に、一段レベルアップできます。

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