正規表現マッチは「文字列の中から“パターン”を見つける」技
文字列分割が「区切りで切る」技だとしたら、正規表現マッチは「ルール(パターン)に合う部分だけを見つける」技です。
「メールアドレスかどうかチェックしたい」「ログから日付だけ抜き出したい」「特定フォーマットのIDだけ拾いたい」――こういうときに真価を発揮します。
ただし、いきなり複雑な正規表現に手を出すと、何を書いているのか自分でも分からなくなります。
だからまずは、「Java で正規表現マッチを扱うための基本パターン」と「それを包むユーティリティ」を押さえるところから始めましょう。
Java の正規表現の基本:Pattern と Matcher
まずは「パターンをコンパイルして、文字列に当てる」
Java で正規表現を扱うときの基本クラスは java.util.regex.Pattern と Matcher です。
最小構成はこうです。
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegexBasicExample {
public static void main(String[] args) {
String regex = "\\d{3}-\\d{4}"; // 例: 123-4567
Pattern pattern = Pattern.compile(regex);
String text = "郵便番号は123-4567です。";
Matcher matcher = pattern.matcher(text);
boolean found = matcher.find();
System.out.println(found); // true
}
}
Javaここで押さえたいのは、次の流れです。
正規表現文字列を Pattern.compile で「パターン」にする。pattern.matcher(text) で、「この文字列に対してマッチングするための Matcher」を作る。matcher.find() や matcher.matches() で、実際にマッチさせる。
この「Pattern と Matcher の二段構え」が、Java の正規表現マッチの基本形です。
業務で使いやすくするためのユーティリティ化
「マッチするかどうか」だけ知りたいときの isMatch
「この文字列がパターンに合っているかどうか」だけ知りたい場面はとても多いです。
毎回 Pattern と Matcher を書くのは冗長なので、ユーティリティにまとめます。
import java.util.regex.Pattern;
public final class Regexes {
private Regexes() {}
public static boolean isMatch(String regex, String text) {
if (regex == null || text == null) {
return false;
}
Pattern pattern = Pattern.compile(regex);
return pattern.matcher(text).matches();
}
}
Java使い方はこうです。
String regex = "\\d{3}-\\d{4}";
System.out.println(Regexes.isMatch(regex, "123-4567")); // true
System.out.println(Regexes.isMatch(regex, "1234-567")); // false
Javaここで深掘りしたい重要ポイントは、「matches() は“文字列全体がパターンに一致するか”を見る」ということです。find() は「どこか一部でも一致すれば true」ですが、matches() は「全部がそのパターンであること」を要求します。
郵便番号やIDのように「文字列全体がこの形式であるべき」というチェックでは、matches() を使うのが基本です。
例題:メールアドレス形式の簡易チェック
完璧を目指さず「業務で十分なレベル」をユーティリティにする
メールアドレスの正規表現は、本気でやるととんでもなく長くなります。
業務では、「明らかにおかしいものを弾ければ十分」という割り切りも大事です。
例えば、次のような簡易パターンを使ってみます(あくまで例です)。
public final class EmailValidator {
private static final Pattern SIMPLE_EMAIL =
Pattern.compile("^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$");
private EmailValidator() {}
public static boolean isValidEmail(String email) {
if (email == null) {
return false;
}
return SIMPLE_EMAIL.matcher(email).matches();
}
}
Java使い方はこうです。
System.out.println(EmailValidator.isValidEmail("user@example.com")); // true
System.out.println(EmailValidator.isValidEmail("user@@example.com")); // false
System.out.println(EmailValidator.isValidEmail("user example.com")); // false
Javaここでのポイントは二つです。
一つ目は、「Pattern を毎回コンパイルせず、static final にして再利用している」ことです。
正規表現のコンパイルはそれなりにコストがかかるので、同じパターンを何度も使うなら、事前にコンパイルしておくのが定石です。
二つ目は、「“完璧な RFC 準拠”ではなく、“業務で許容できるレベル”のパターンをユーティリティとして固定している」ことです。
これにより、「プロジェクト内でメールアドレスのチェック方法がバラバラ」という状態を防げます。
例題:ログから特定の情報を抜き出す(グルーピング)
キャプチャグループで「欲しい部分だけ」取り出す
正規表現の強みは、「パターンに合うかどうか」だけでなく、「その中の一部を抜き出せる」ことです。
例えば、次のようなログ行があるとします。
2025-01-30 12:34:56 INFO userId=u-001 action=LOGIN
ここから「日付」「ログレベル」「userId」「action」を取り出したいとします。
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public final class LogLineParser {
// 日付 時刻 レベル userId=... action=...
private static final Pattern LOG_PATTERN = Pattern.compile(
"^(\\d{4}-\\d{2}-\\d{2})\\s+" + // 1: 日付
"(\\d{2}:\\d{2}:\\d{2})\\s+" + // 2: 時刻
"(\\S+)\\s+" + // 3: レベル
"userId=(\\S+)\\s+" + // 4: userId
"action=(\\S+)$" // 5: action
);
private LogLineParser() {}
public static LogRecord parse(String line) {
Matcher m = LOG_PATTERN.matcher(line);
if (!m.matches()) {
throw new IllegalArgumentException("ログ形式が不正です: " + line);
}
String date = m.group(1);
String time = m.group(2);
String level = m.group(3);
String userId = m.group(4);
String action = m.group(5);
return new LogRecord(date, time, level, userId, action);
}
public record LogRecord(String date, String time, String level,
String userId, String action) {}
}
Java使い方はこうです。
String line = "2025-01-30 12:34:56 INFO userId=u-001 action=LOGIN";
LogLineParser.LogRecord r = LogLineParser.parse(line);
System.out.println(r.date()); // 2025-01-30
System.out.println(r.time()); // 12:34:56
System.out.println(r.level()); // INFO
System.out.println(r.userId()); // u-001
System.out.println(r.action()); // LOGIN
Javaここで深掘りしたい重要ポイントは、「キャプチャグループ((...))の順番と group(n) の対応を、コードとして固定している」ことです。
正規表現だけを見ると何番目が何か分かりにくいですが、LogRecord のフィールド名と group(1)〜group(5) の対応をきちんと書いておくことで、
「このパターンは何を抜き出しているのか」がコードから読み取れるようになります。
例題:文字列中のすべてのマッチを列挙する
find をループして「全部拾う」パターン
matches() は「全体が一致するか」を見るメソッドでしたが、
「文字列の中に何回も出てくるパターンを全部拾いたい」というときは find() を使います。
例えば、「テキスト中のすべてのメールアドレスっぽいものを拾う」例です(パターンは簡易版)。
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public final class EmailExtractor {
private static final Pattern SIMPLE_EMAIL =
Pattern.compile("[^@\\s]+@[^@\\s]+\\.[^@\\s]+");
private EmailExtractor() {}
public static List<String> extractAll(String text) {
List<String> result = new ArrayList<>();
if (text == null || text.isEmpty()) {
return result;
}
Matcher m = SIMPLE_EMAIL.matcher(text);
while (m.find()) {
result.add(m.group());
}
return result;
}
}
Java使い方はこうです。
String text = "連絡先はuser1@example.comとuser2@test.orgです。";
List<String> emails = EmailExtractor.extractAll(text);
System.out.println(emails); // [user1@example.com, user2@test.org]
Javaここでのポイントは、「find() をループし、group() で“今マッチした部分”を取り出す」というパターンを覚えることです。matches() は「全体一致」、find() は「部分一致を順番に拾う」と覚えておくと、使い分けがスムーズになります。
正規表現マッチでつまずきやすいポイント
バックスラッシュと Java 文字列リテラルの二重苦
Java の正規表現は、**「正規表現」と「Java の文字列リテラル」の二重の世界」を同時に扱う必要があります。
例えば、「数字1文字」を表す正規表現は \d ですが、Java の文字列リテラルでは \ 自体がエスケープ文字です。
そのため、コード上では "\\d" と書かなければなりません。
同様に、「バックスラッシュそのもの」を表したいときは、正規表現では \\、Java 文字列では "\\\\" になります。
最初は混乱しやすいので、「正規表現の世界ではこう、Java の世界ではこう」と紙に書き出して整理してみると、だいぶ楽になります。
複雑になりすぎたら「名前をつけて分割する」
正規表現は、欲張るとすぐに「暗号」になります。
例えば、1行で全部書こうとするのではなく、
日付部分のパターン
時刻部分のパターン
ログレベル部分のパターン
のように、意味ごとに文字列定数を分けてから + でつなぐと、かなり読みやすくなります。
private static final String DATE = "(\\d{4}-\\d{2}-\\d{2})";
private static final String TIME = "(\\d{2}:\\d{2}:\\d{2})";
private static final String LEVEL = "(\\S+)";
private static final Pattern LOG_PATTERN = Pattern.compile(
"^" + DATE + "\\s+" + TIME + "\\s+" + LEVEL + ".*$"
);
Java「正規表現を分割して名前をつける」というだけで、
「何をマッチさせたいのか」が一気にクリアになります。
まとめ:正規表現マッチユーティリティで身につけたい感覚
正規表現マッチは、「文字列の中から“ルールに合う部分”を見つけたり抜き出したりする」ための強力な技です。
押さえておきたい感覚は、まず「Pattern と Matcher の二段構えを理解し、matches()(全体一致)と find()(部分一致)の役割を区別する」こと。
次に、「よく使うパターン(メールアドレス、ログ行、ID など)は static final Pattern としてユーティリティに閉じ込め、プロジェクト全体で同じチェック・抽出ロジックを共有する」こと。
そして、「複雑な正規表現は意味ごとに分割して名前をつけ、キャプチャグループとオブジェクトのフィールドをきちんと対応づける」ことです。
もしあなたのコードのどこかに、text.matches("...") や Pattern.compile("...") が生で散らばっているなら、
その一つを題材にして、ここで作った Regexes.isMatch や EmailValidator、LogLineParser のようなユーティリティに置き換えてみてください。
それだけで、「読めて、直せて、再利用しやすい正規表現マッチ」に、一段レベルアップできます。
