MEDIA

メディア

  1. TOP
  2. メディア
  3. プログラミング
  4. Java正規表現を完全ガイド|Pattern・Matcherの基本と実務での注意点

Java正規表現を完全ガイド|Pattern・Matcherの基本と実務での注意点

Javaの開発現場において、正規表現(Regular Expressions / Regex)は避けて通れない技術の一つです。入力フォームのバリデーション、ログファイルの解析、文字列の置換など、活用シーンは多岐にわたります。

しかし、Javaの正規表現には独特の難しさがあります。

\(バックスラッシュ)をいくつ書けばいいのか分からない」
PatternMatcher の使い分けが曖昧」
「コピペした正規表現がなぜか動かない」

このように感じるのは、正規表現自体の複雑さに加え、Java特有の「文字列表現のルール」が絡み合っているためです。

そこで本記事では、Javaにおける正規表現の基本的な仕組みから、実務で必ず直面する「二重エスケープ問題」、そしてパフォーマンスとセキュリティを意識した実装方法までを体系的に解説します。

Java正規表現の基本構造:PatternとMatcher

Javaで正規表現を扱う際、主役となるのは java.util.regex パッケージにある2つのクラス、PatternMatcher です。

これらは、料理で例えると「金型(Pattern)」と「クッキー生地(Matcher)」のような関係にあります。

  • Pattern(金型):正規表現のルールをコンパイル(解析)して、再利用可能な状態にしたもの
  • Matcher(生地への適用):その金型を使って、実際の文字列を判定・操作するエンジン

基本的な実装ステップ

最も標準的な使い方は、以下の3ステップです。

  1. Pattern.compile() で正規表現を定義する
  2. pattern.matcher() で検索対象の文字列をセットする
  3. matcher.matches()find() で判定・検索を行う

具体的なコード例を見てみましょう。

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

public class RegexSample {
    public static void main(String[] args) {
        // 1. 正規表現をコンパイル("^\d+$" は数字のみ)
        // ※Javaではバックスラッシュを2つ書く必要がある(後述)
        Pattern p = Pattern.compile("^\\d+$");

        // 2. 判定したい文字列を渡してMatcherを作成
        Matcher m = p.matcher("12345");

        // 3. 判定を実行
        if (m.matches()) {
            System.out.println("数字のみです");
        } else {
            System.out.println("数字以外が含まれています");
        }
    }
}

このように、手順を分けて記述することで、同じパターンを使って複数の文字列を高速にチェックすることが可能になります。

参考リンク:Oracle公式 Patternクラスドキュメント

初心者の最大の壁「二重エスケープ」の罠

Javaの正規表現を学ぶ際、最も混乱するのが「バックスラッシュ(\)の扱い」です。

正規表現では、「数字」を表すために \d という記号を使います。しかし、Javaのコード内で "\d" と書くとコンパイルエラーになります。なぜでしょうか?

Javaの文字列としての解釈と、正規表現としての解釈

理由は、Javaの文字列リテラル("...")において、バックスラッシュ自体が「エスケープ文字」として扱われるからです。

  1. 正規表現エンジンは、「数字」を判定するために \d という文字を欲しがっている
  2. しかし、Javaコンパイラ\ を見ると「次の文字を特殊文字(改行\nなど)として扱う」と解釈する
  3. そのため、\ という文字そのものをJavaの文字列として表現するには、\\ と記述する必要がある

つまり、以下のような変換が行われています。

やりたいこと 正規表現での記法 Javaコードでの記述
数字を表す \d "\\d"
空白文字を表す \s "\\s"
ドット(.)そのもの \. "\\."
円記号(\)そのもの \\ "\\\\"

この「二重エスケープ」はJava特有のルールであり、他の言語(JavaScriptやPHPなど)の経験者が最もつまずくポイントです。

matches() と find() の決定的な違い

次に多いミスが、matches()find() の混同です。どちらもマッチングを行いますが、対象範囲が異なります。

  • matches():文字列全体がパターンに一致する場合のみ true
  • find():文字列の一部にパターンが含まれていれば true
Pattern p = Pattern.compile("\\d+"); // 数字の連続
Matcher m = p.matcher("ID: 12345");

// matches() は全体("ID: 12345")を見るため false
System.out.println(m.matches()); // false

// find() は部分("12345")を見つけるため true
m.reset(); // カーソルを戻す
System.out.println(m.find());    // true

バリデーション(入力チェック)には matches()、テキストからの抽出や検索には find() を使う、と覚えておきましょう。

実務で使える基本パターン一覧

ここでは、実務で頻繁に使用されるパターンを紹介します。そのままコピーして使えるよう、Javaのエスケープ済み表記で記載しています。

1. 半角数字のみ

"^[0-9]+$"  // または "^\\d+$"

2. 半角英数字(IDなど)

"^[a-zA-Z0-9]+$"

3. 日付(yyyy-MM-dd)

"^\\d{4}-\\d{2}-\\d{2}$"

※正規表現だけでは「2月30日」のような不正な日付は弾けないため、別途 LocalDate などでチェックするのが定石です。

4. 簡易的なメールアドレス

"^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$"

実務レベルのベストプラクティスと注意点

最後に、プロの現場で意識すべき「パフォーマンス」と「可読性」のポイントを解説します。

1. Patternは「事前コンパイル」する

Pattern.compile() はコストが高い(重い)処理です。メソッドが呼ばれるたびにコンパイルすると、アプリケーション全体の速度低下を招きます。

そのため、以下のように static final フィールドとして定数化し、再利用するのが鉄則です。

public class UserValidator {
    // クラスロード時に1回だけコンパイルされる
    private static final Pattern EMAIL_PATTERN = 
        Pattern.compile("^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$");

    public boolean isValidEmail(String email) {
        return EMAIL_PATTERN.matcher(email).matches();
    }
}

2. 複雑なパターンにはコメントモードを使う

正規表現は長くなると解読不能になります。Pattern.COMMENTS フラグを使うと、正規表現の中にスペースやコメントを書くことができ、可読性が劇的に向上します。

Pattern p = Pattern.compile(
    "^[A-Za-z0-9._%+-]+@" +  // ローカル部
    "[A-Za-z0-9.-]+\\." +    // ドメイン
    "[A-Za-z]{2,}$",         // TLD
    Pattern.COMMENTS
);

3. ReDoS(正規表現によるDoS攻撃)に注意

近年、セキュリティ診断でよく指摘されるのが ReDoS です。これは、極端に処理時間の長い文字列を送りつけ、正規表現エンジンの「バックトラック(後戻り処理)」を悪用してCPUを占有させる攻撃です。

(a+)+ のようなネストした量指定子は避け、必要であれば入力文字数の上限を設けるなどの対策を行いましょう。

参考リンク:OWASP - Regular expression Denial of Service (ReDoS)

まとめ

本記事では、Javaの正規表現について解説しました。

  • 基本構造Pattern(金型)を作って Matcher(判定)を行う
  • 注意点:Javaではバックスラッシュを \\ と二重に書く必要がある
  • 使い分け:全体一致なら matches()、部分検索なら find()
  • 最適化Patternstatic final で定数化して使い回す

正規表現は強力な武器ですが、書き方を誤るとバグや脆弱性の原因にもなります。まずは基本的なパターンから使いこなし、少しずつ複雑な表現に挑戦してみてください。

もし、実際のアプリケーション開発で「複雑なバリデーション設計に悩んでいる」「セキュリティリスクがないかコードレビューしてほしい」といった課題をお持ちなら、経験豊富なエンジニアに相談するのが解決への近道です。

つまずいたら、現役エンジニアと一緒に解決しよう

この記事で扱った【Java】は、独学だと細部で詰まりやすい分野です。さらに理解を深めたい方は、Zerocode Onlineで実務直結の学習を進めてみませんか。

現役エンジニア1on1

コード添削・詰まり解消を丁寧にサポート。

実務設計まで網羅

Java/SQL/Spring Bootを体系的に習得。

ポートフォリオ支援

現場で刺さる成果物づくりを伴走。

完全オンライン

時間と場所を選ばず学べます。

Join us! 未経験からエンジニアに挑戦できる環境で自分の可能性を信じてみよう 採用ページを見る→

記事監修

ドライブライン編集部

[ この記事をシェアする ]

記事一覧へ戻る