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

Java Java
スポンサーリンク

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

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

ファイル名が .csv で終わるものだけ処理したい。
メールアドレスが @example.com で終わるか確認したい。
URLが /api で終わるものだけ特別扱いしたい。

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


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

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

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

public final class SuffixMatcher {

    private SuffixMatcher() {}

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

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

System.out.println(SuffixMatcher.endsWith("report.csv", ".csv")); // true
System.out.println(SuffixMatcher.endsWith("image.png", ".csv"));  // false
System.out.println(SuffixMatcher.endsWith("user@example.com", "@example.com")); // true
System.out.println(SuffixMatcher.endsWith("user@other.com", "@example.com"));   // false
System.out.println(SuffixMatcher.endsWith(null, ".csv"));                       // false
System.out.println(SuffixMatcher.endsWith("report.csv", null));                 // false
Java

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

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

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


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

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

英字を扱うときに必ず出てくるのが「大文字小文字を区別するかどうか」です。
例えば、拡張子 .CSV.csv.CsV も、全部「CSVファイル」として扱いたい、というケースはよくあります。

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

public static boolean endsWithIgnoreCase(String text, String suffix) {
    if (text == null || suffix == null) {
        return false;
    }
    String t = text.toLowerCase();
    String s = suffix.toLowerCase();
    return t.endsWith(s);
}
Java

使い方はこうです。

System.out.println(
    SuffixMatcher.endsWithIgnoreCase("report.CSV", ".csv")
); // true

System.out.println(
    SuffixMatcher.endsWithIgnoreCase("report.csv", ".CSV")
); // true

System.out.println(
    SuffixMatcher.endsWithIgnoreCase("image.png", ".csv")
); // false
Java

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

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

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

というだけです。

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


前後の空白や改行をどう扱うか

「見た目としてそのサフィックスで終わっていればOK」にしたい場合

外部ファイルやユーザー入力では、
末尾に空白や改行が紛れ込むことがよくあります。

例えば、"report.csv "(末尾に空白)や "report.csv\n"(末尾に改行)も、
.csv で終わっている」とみなしたいことがあります。

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

public static boolean endsWithTrimmed(String text, String suffix) {
    if (text == null || suffix == null) {
        return false;
    }
    String t = text.strip();   // 前後の空白(全角スペースや改行も含む)を削除(Java 11以降)
    String s = suffix.strip();
    return t.endsWith(s);
}
Java

使い方はこうです。

System.out.println(
    SuffixMatcher.endsWithTrimmed("report.csv ", ".csv")
); // true

System.out.println(
    SuffixMatcher.endsWithTrimmed("report.csv\n", ".csv")
); // true

System.out.println(
    SuffixMatcher.endsWithTrimmed("report.csv\t", ".csv")
); // true
Java

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

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

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


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

例1:ファイル拡張子チェックユーティリティ

ファイルアップロード機能などでは、
「CSVだけ受け付ける」「画像ファイルだけ受け付ける」といったチェックがよく出てきます。

後方一致を使って、拡張子チェックのユーティリティを書いてみます。

public final class FileTypeChecker {

    private FileTypeChecker() {}

    public static boolean isCsv(String fileName) {
        if (fileName == null || fileName.isBlank()) {
            return false;
        }
        return SuffixMatcher.endsWithIgnoreCase(fileName.strip(), ".csv");
    }

    public static boolean isImage(String fileName) {
        if (fileName == null || fileName.isBlank()) {
            return false;
        }
        String name = fileName.strip();
        return SuffixMatcher.endsWithIgnoreCase(name, ".png")
                || SuffixMatcher.endsWithIgnoreCase(name, ".jpg")
                || SuffixMatcher.endsWithIgnoreCase(name, ".jpeg")
                || SuffixMatcher.endsWithIgnoreCase(name, ".gif");
    }
}
Java

使い方はこうです。

System.out.println(FileTypeChecker.isCsv("report.csv"));    // true
System.out.println(FileTypeChecker.isCsv("report.CSV "));   // true
System.out.println(FileTypeChecker.isCsv("report.txt"));    // false

System.out.println(FileTypeChecker.isImage("photo.png"));   // true
System.out.println(FileTypeChecker.isImage("photo.JPG"));   // true
System.out.println(FileTypeChecker.isImage("document.pdf"));// false
Java

ここでの重要ポイントは、「低レベルな後方一致を“意味のある判定”に昇華している」ことです。
単に endsWith を呼ぶのではなく、
「CSVかどうか」「画像ファイルかどうか」というビジネス的な意味に変換しています。

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

例2:ドメイン制限付きメールアドレスチェック

「自社ドメインのメールアドレスだけ許可したい」という要件もよくあります。

例えば、@example.com で終わるメールアドレスだけOKにするユーティリティは、こう書けます。

public final class MailDomainChecker {

    private MailDomainChecker() {}

    public static boolean isFromCompanyDomain(String email) {
        if (email == null || email.isBlank()) {
            return false;
        }
        String normalized = email.strip().toLowerCase();
        return normalized.endsWith("@example.com");
    }
}
Java
System.out.println(
    MailDomainChecker.isFromCompanyDomain("user@example.com")
); // true

System.out.println(
    MailDomainChecker.isFromCompanyDomain("USER@EXAMPLE.COM ")
); // true

System.out.println(
    MailDomainChecker.isFromCompanyDomain("user@other.com")
); // false
Java

ここでも、「strip で前後の空白を削る」「toLowerCase で小文字に揃える」という正規化をしてから、
後方一致で判定しています。


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

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

まずは

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

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

そのうえで、

拡張子チェック
メールアドレスのドメイン制限
特定のサフィックスを持つIDやURLの判定

のような「意味のある判定」に組み込んでいくと、
後方一致が単なる文字列操作ではなく、
業務ロジックの一部としてしっかり機能し始めます。

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

if (fileName.endsWith(".csv")) { ... }

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

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

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