Java Tips | 文字列処理:前方一致

Java Java
スポンサーリンク

前方一致は「この文字列で始まっているか」を見るシンプルな判定

前方一致は、
「文字列が、ある決まった文字列で“始まっているか”どうか」を調べるテクニックです。

商品コードが ABC で始まるものだけ抽出したい。
ログの行が ERROR で始まるものだけ拾いたい。
URLが https:// で始まるか確認したい。

こういうときに使うのが前方一致です。
Javaでは String#startsWith が用意されていて、これをユーティリティとして包んでおくと、
業務コードがかなり読みやすくなります。


基本形:startsWith をそのままラップする

一番シンプルな前方一致ユーティリティ

まずは、Java標準の startsWith をそのまま使う形からいきます。

public final class PrefixMatcher {

    private PrefixMatcher() {}

    public static boolean startsWith(String text, String prefix) {
        if (text == null || prefix == null) {
            return false;
        }
        return text.startsWith(prefix);
    }
}
Java

使い方はこんな感じです。

System.out.println(PrefixMatcher.startsWith("ABC123", "ABC")); // true
System.out.println(PrefixMatcher.startsWith("XYZ123", "ABC")); // false
System.out.println(PrefixMatcher.startsWith("ERROR: something", "ERROR")); // true
System.out.println(PrefixMatcher.startsWith("INFO: something", "ERROR"));  // false
System.out.println(PrefixMatcher.startsWith(null, "ABC"));                 // false
System.out.println(PrefixMatcher.startsWith("ABC123", null));              // false
Java

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

一つ目は、「null の扱いをユーティリティ側で決めている」ことです。
textprefix が null のときにどうするかを、
「false を返す」というポリシーで固定しています。
これを決めておくと、呼び出し側で毎回 null チェックを書かなくて済みます。

二つ目は、「業務コードから startsWith の生呼び出しを隠している」ことです。
生で text.startsWith("ABC") をあちこちに書くと、
null チェックの抜けや、大小文字の扱いのバラつきが出やすくなります。
ユーティリティにまとめることで、「前方一致のルール」を一箇所に集約できます。


大文字小文字を無視した前方一致

toLowerCase / toUpperCase で正規化してから比較する

英字を扱うときに必ず出てくるのが「大文字小文字を区別するかどうか」です。
例えば、ログの行が error でも ERROR でも Error でも、
全部「エラー行」として扱いたい、というケースはよくあります。

その場合は、両方を同じ大文字・小文字に揃えてから startsWith します。

public static boolean startsWithIgnoreCase(String text, String prefix) {
    if (text == null || prefix == null) {
        return false;
    }
    String t = text.toLowerCase();
    String p = prefix.toLowerCase();
    return t.startsWith(p);
}
Java

使い方はこうです。

System.out.println(
    PrefixMatcher.startsWithIgnoreCase("ERROR: something", "error")
); // true

System.out.println(
    PrefixMatcher.startsWithIgnoreCase("Error: something", "ERROR")
); // true

System.out.println(
    PrefixMatcher.startsWithIgnoreCase("INFO: ok", "error")
); // false
Java

ここで深掘りしたい重要ポイントは、「正規化(normalize)してから判定する」という考え方です。

やっていることはシンプルで、

文字列全体を小文字に変換する
プレフィックスも小文字に変換する
そのうえで startsWith する

というだけです。

この「前処理(正規化)+本処理(前方一致)」という分け方は、
部分一致・完全一致・検索など、文字列処理全般でとても重要なパターンです。
「何を無視して、何を区別するか」を前処理で決めてしまうと、
本処理のコードがスッキリします。


前後の空白や全角スペースをどう扱うか

「見た目としてそのプレフィックスで始まっていればOK」にしたい場合

ユーザー入力や外部データでは、
先頭に空白(半角・全角)が紛れ込むことがよくあります。

例えば、" ERROR: something"(先頭に空白)というログ行も、
「ERROR で始まっている」とみなしたいことがあります。

その場合は、前方一致の前に「空白を取り除く」正規化を挟みます。

public static boolean startsWithTrimmed(String text, String prefix) {
    if (text == null || prefix == null) {
        return false;
    }
    String t = text.strip();   // 前後の空白(全角スペースも含む)を削除(Java 11以降)
    String p = prefix.strip();
    return t.startsWith(p);
}
Java
System.out.println(
    PrefixMatcher.startsWithTrimmed("  ERROR: something", "ERROR")
); // true

System.out.println(
    PrefixMatcher.startsWithTrimmed(" ERROR: something", "ERROR")
); // true(全角スペースも削除される)
Java

ここでのポイントは、「どこまで正規化するかは要件次第」ということです。

前後の空白だけを無視するのか、
タブや改行も無視するのか、
全角・半角の違いも吸収するのか。

全部を一気にやろうとすると複雑になるので、
まずは「前後の空白を削る」「大文字小文字を揃える」くらいから始めて、
必要に応じて正規化の範囲を広げていくのが現実的です。


実務での前方一致の使いどころ

コード例:ログレベル判定ユーティリティ

前方一致は、業務コードの中で「条件分岐の軸」としてよく使われます。
例えば、ログの1行からログレベルを判定するユーティリティを考えてみましょう。

public enum LogLevel {
    ERROR, WARN, INFO, DEBUG, UNKNOWN
}

public final class LogLevelDetector {

    private LogLevelDetector() {}

    public static LogLevel detect(String logLine) {
        if (logLine == null || logLine.isBlank()) {
            return LogLevel.UNKNOWN;
        }
        String line = logLine.strip(); // 前後の空白を削る

        if (line.startsWith("ERROR")) {
            return LogLevel.ERROR;
        }
        if (line.startsWith("WARN")) {
            return LogLevel.WARN;
        }
        if (line.startsWith("INFO")) {
            return LogLevel.INFO;
        }
        if (line.startsWith("DEBUG")) {
            return LogLevel.DEBUG;
        }
        return LogLevel.UNKNOWN;
    }
}
Java

使い方はこうです。

System.out.println(LogLevelDetector.detect("ERROR: something")); // ERROR
System.out.println(LogLevelDetector.detect("WARN: something"));  // WARN
System.out.println(LogLevelDetector.detect("INFO: ok"));         // INFO
System.out.println(LogLevelDetector.detect("DEBUG: detail"));    // DEBUG
System.out.println(LogLevelDetector.detect("TRACE: detail"));    // UNKNOWN
Java

ここでの重要ポイントは、「前方一致を“意味のある分類”に使っている」ことです。
単に true/false を返すだけでなく、
「どのログレベルか」というビジネス的な意味に変換しています。

こうやって、「低レベルな文字列操作(startsWith)」を
「高レベルなドメインの概念(LogLevel)」に橋渡しするのが、
実務ユーティリティの本当の価値です。


まとめ:前方一致ユーティリティで身につけたい感覚

前方一致は、「この文字列が、指定した文字列で始まっているか」を見る、
とてもシンプルなテクニックです。

まずは

startsWith をそのままラップした基本版
大文字小文字を無視する startsWithIgnoreCase
前後の空白を削ってから判定する startsWithTrimmed

といったユーティリティを用意して、
「null の扱い」「正規化の方針」を自分のプロジェクト内で統一してみてください。

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

if (line.startsWith("ERROR")) { ... }
Java

のような生の条件が散らばっているなら、
それを PrefixMatcherLogLevelDetector のようなユーティリティに一度集約してみるといいです。

その小さな整理が、
「読みやすくて変更に強い文字列処理」を書けるエンジニアへの、確かな一歩になります。

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