Javaで開発をしていると、必ずと言っていいほど出てくるのが「入力値のチェック」である。
メールアドレス、電話番号、英数字、パスワード。
これらを正しく検証するために使われるのが、正規表現(Regular Expression)だ。
しかし、Javaの正規表現は初心者がつまずきやすい。
Pattern?
Matcher?
matches?
find?
ネットの記事も断片的な説明ばかりで、実務でどう書くべきかが見えてこない。
そこでこの記事では、Javaで正規表現チェックを行う方法を、基礎から実務レベルまで体系的に解説する。
Javaの正規表現は、正しく理解すれば非常に強力な武器になる。
逆に曖昧な理解のまま使うと、バグの温床になる。
ここで一度、きちんと整理しておくべきである。
執筆者:江田島 |
|
Javaで正規表現チェックを行う基本構造
Javaで正規表現を扱う場合、基本となるクラスは2つだ。
- Pattern
- Matcher
この2つの役割を理解することが、すべての出発点になる。
まずは最も基本的なコードを見てほしい。
import java.util.regex.Pattern;
import java.util.regex.Matcher;
public class Main {
public static void main(String[] args) {
String regex = "^[0-9]+$";
String target = "12345";
// 正規表現をコンパイル
Pattern pattern = Pattern.compile(regex);
// 評価器を生成
Matcher matcher = pattern.matcher(target);
// 判定実行
boolean result = matcher.matches();
System.out.println(result);
}
}
このコードは「文字列が数字のみで構成されているか」をチェックしている。
出力結果は以下になる。
true
では、このコードが何をしているのかを分解して理解しよう。
Pattern:正規表現をコンパイルする
まず登場するのがPatternクラスである。
Pattern pattern = Pattern.compile(regex);
ここでは「正規表現の文字列」を解析し、内部的な「オブジェクト」を作っている。
言い換えるなら、正規表現の設計図を作る処理である。
重要なのはここだ。
Javaでは、正規表現は毎回解析されるとコストが高い。
そのため、Patternを使って事前にコンパイルする仕組みになっている。
この設計を理解していないエンジニアが多いため、無駄にPatternを何度も作るコードを書く。
それは非効率の極みだ。
正規表現は、再利用できるなら必ず再利用する。
これは実務の基本である。
Matcher:対象文字列を評価する
次に登場するのがMatcherクラスだ。
Matcher matcher = pattern.matcher(target);
これは「この文字列を、この正規表現でチェックする」という評価器を作っている。
つまり、「Pattern(設計図)」を「Matcher(検査員)」に渡してチェックさせる、という流れになる。
matches():文字列全体が一致するかチェック
最後に使うのが matches() メソッド。
boolean result = matcher.matches();
これは、「文字列全体が正規表現に一致するか」を判定する。
ここを誤解しているエンジニアが多いが、matches() は部分一致ではない。
完全一致 である。
例えば、正規表現が ^[0-9]+$(数字のみ)の場合、文字列が「123abc」なら結果は「false」になる。
matchesとfindの違いを理解していないコードは危険
Javaの正規表現でよくあるミスがこれだ。
「matches」と「find」の違いを理解していない。
この2つは全く意味が違う。
matches:全文一致
matcher.matches()
■意味:「文字列全体が正規表現に一致する」
例としては、正規表現 [0-9]+に対して、文字列「abc123」を判定すると、結果は「false」になる、ということが挙げられる。
なぜなら、文字列全体が数字ではないからだ。
find:部分一致
一方、find は違う。
matcher.find()
■意味:「文字列のどこかに一致する部分があるか」をチェックする。
例としては以下の通り。
String regex = "[0-9]+";
String target = "abc123xyz";
この結果は「true」になる。
理由は単純で、文字列の中に「123」という一致部分があるからだ。
この違いを理解していないコードは、実務では非常に危険である。
入力チェックでfindを使えば、簡単にバリデーションを突破されるバグが生まれる。
入力バリデーションではmatchesを使う。
これは覚えておくべき鉄則だ。
Pattern.matchesを使う簡易チェック
実はJavaには、もっと短く書く方法もある。
それがPattern.matches()である。
import java.util.regex.Pattern;
public class Main {
public static void main(String[] args) {
// コンパイルから実行まで1行で済ませる
boolean result = Pattern.matches("^[0-9]+$", "12345");
System.out.println(result);
}
}
これは内部で「Pattern生成」「Matcher生成」「matches実行」をまとめて行ってくれる。
ただし注意点がある。
この方法は、毎回Patternを生成する。
つまり、大量処理では劇的に遅くなる。
■単発チェック → Pattern.matches
■繰り返し処理 → Pattern.compile(定数化して再利用)
これが正しい使い分けである。
Javaでよく使う正規表現チェック例
ここからは、実務でよく使う正規表現チェックを紹介する。
WordPressなどのCMS環境での表示崩れを防ぐため、バックスラッシュを伴う \d ではなく文字クラス [0-9] を使用した記法を用いる。
■数字のみチェック
String regex = "^[0-9]+$";
boolean result = Pattern.matches(regex, "123456");
文字列の開始から終了まで、すべて数字であることをチェックしている。
■英数字チェック
String regex = "^[a-zA-Z0-9]+$";
これは英字と数字のみを許可するパターンだ。
ログインIDなどの検証によく使われる。
■メールアドレスチェック
簡易的なチェックなら次で十分だ。
String regex = "^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+$";
ただし、正規表現だけで完全なメールチェックはできない。
実務では「簡易正規表現」と「メール送信確認」の2段階で検証するのが現実的である。
実務で正規表現を使うときの鉄則
最後に、現場で何度も見てきた失敗をまとめておく。
正規表現は便利だが、扱いを間違えると保守性が地獄になる。
正規表現は必ず定数に定義する
悪いコード。
Pattern.matches("^[0-9]{8}[A-Z]{3}$", text);
これでは何をチェックしているのか分からない。
良いコードは、こうなる。
private static final String PRODUCT_CODE_REGEX = "^[0-9]{8}[A-Z]{3}$";
Pattern.matches(PRODUCT_CODE_REGEX, text);
意味が分かる名前を付ける。
これはエンジニアの基本だ。
Patternは使い回す
これはパフォーマンスの話だ。
悪いコード(ループ内で毎回生成)。
for(String text : list){
Pattern.matches(regex, text); // 遅い
}
良いコード(事前にコンパイル)。
Pattern pattern = Pattern.compile(regex);
for(String text : list){
Matcher matcher = pattern.matcher(text);
matcher.matches();
}
まとめ
Javaで正規表現チェックを行う基本は以下の通りだ。
- 正規表現はPatternでコンパイルする
- Matcherで文字列を評価する
- matchesは全文一致、findは部分一致
- 繰り返し処理ではPatternを再利用する
正規表現は便利な道具だが、雑に使えばコードの可読性を破壊する。
短いから正義ではない。
読めるコードこそ正義である。
これを理解しているエンジニアだけが、正規表現を正しく「道具」として使いこなせる。



コメント