C# を使用したアプリケーション開発において、入力された文字列が特定の形式(メールアドレス、電話番号、郵便番号など)に従っているかをチェックする処理は極めて一般的です。
そのような「パターン一致」の判定を効率的に行うための標準的な手段が、System.Text.RegularExpressions 名前空間に用意されている Regex.IsMatch メソッド です。
正規表現(Regular Expression)は強力なライブラリですが、その使いこなし方一つでプログラムの可読性やパフォーマンスは大きく変わります。
本記事では、Regex.IsMatch の基本的な使い方から、パフォーマンスを最適化するための高度なテクニック、そしてモダンな C# で推奨される Source Generator を活用した実装方法までを徹底的に解説します。

Regex.IsMatch の基本的な使い方
C# で正規表現によるパターン一致を判定する際、最も手軽に利用できるのが Regex.IsMatch 静的メソッドです。
このメソッドは、対象の文字列が指定された正規表現パターンに 少なくとも1箇所で一致するかどうか を bool 型で返します。
まずは、最もシンプルなコード例を見てみましょう。
using System;
using System.Text.RegularExpressions;
public class Example
{
public static void Main()
{
string input = "Hello, World!";
string pattern = "World";
// 静的メソッドによる判定
bool isMatch = Regex.IsMatch(input, pattern);
if (isMatch)
{
Console.WriteLine("一致する箇所が見つかりました。");
}
}
}
この例では、文字列 “Hello, World!” の中に “World” というパターンが含まれているかを判定しています。
結果は true となります。
必須のインポート
正規表現を使用するには、コードの冒頭で using System.Text.RegularExpressions; を記述する必要があります。
これがないと Regex クラスを参照できないため注意してください。
逐語的文字列リテラル (@) の活用
C# で正規表現パターンを記述する際は、逐語的文字列リテラル(@””) を使用することが推奨されます。
正規表現ではバックスラッシュ(\)を多用しますが、通常の文字列ではバックスラッシュがエスケープシーケンスとして扱われてしまうためです。
// 推奨される書き方
string pattern = @"\d{3}-\d{4}"; // 数字3桁-数字4桁
// 推奨されない書き方(バックスラッシュを二重にする必要がある)
string pattern = "\\d{3}-\\d{4}";

静的メソッドとインスタンスメソッドの使い分け
Regex.IsMatch には、静的(static)メソッド として呼び出す方法と、Regex クラスを インスタンス化 してから呼び出す方法の2通りがあります。
これらは動作は同じですが、内部的な処理とパフォーマンス特性が異なります。
静的メソッドによる呼び出し
前述の例のように、Regex.IsMatch(input, pattern) と直接呼び出す方法です。
- メリット
コードが簡潔になり、使い捨ての判定に適している。
- デメリット
内部でパターンのキャッシュが行われるものの、頻繁に異なるパターンを呼び出す場合はオーバーヘッドが生じる可能性がある。
インスタンスメソッドによる呼び出し
new Regex(pattern) でオブジェクトを生成してからメソッドを呼び出す方法です。
- メリット
同じパターンを繰り返し利用する場合、コンパイル済みの正規表現を再利用できるため効率が良い。
- デメリット
インスタンスの管理が必要になる。
// インスタンス化して利用
var regex = new Regex(@"^\d+$"); // 数字のみ
bool result = regex.IsMatch("12345");
大量のデータをループ内で処理する場合 などは、インスタンスを一度だけ生成して使い回す方が、パフォーマンス面で有利になります。
RegexOptions による動作のカスタマイズ
正規表現の判定挙動を細かく制御するために、RegexOptions 列挙型を指定できます。
特によく使われるオプションを以下の表にまとめました。
| オプション | 内容 |
|---|---|
RegexOptions.IgnoreCase | 大文字と小文字を区別せずに検索します。 |
RegexOptions.Multiline | ^ と $ が行の始まりと終わりに一致するようになります。 |
RegexOptions.Singleline | . が改行文字(\n)を含むすべての文字に一致するようになります。 |
RegexOptions.Compiled | 正規表現を MSIL コードにコンパイルし、実行速度を向上させます(起動時間は増えます)。 |
使用例:大文字小文字を区別しない
string input = "ABC";
string pattern = "abc";
// オプションなしでは false
bool match1 = Regex.IsMatch(input, pattern);
// IgnoreCase を指定すれば true
bool match2 = Regex.IsMatch(input, pattern, RegexOptions.IgnoreCase);
現代的な C# で推奨される [GeneratedRegex] (C# 11以降)
.NET 7 および C# 11 以降では、ソースジェネレーター(Source Generators) を利用した正規表現の最適化が可能になりました。
これが現代の C# 開発において 最も推奨される方法 です。
従来の RegexOptions.Compiled は実行時にコンパイルを行っていましたが、[GeneratedRegex] 属性を使用すると、ビルド時 に正規表現の解析コードが自動生成されます。
これにより、実行時のパフォーマンスが劇的に向上し、アプリの起動時間も短縮されます。

[GeneratedRegex] の実装方法
ソースジェネレーターを利用するには、パーシャルクラス(partial class)内で、[GeneratedRegex] 属性を付与したパーシャルメソッドを定義します。
using System.Text.RegularExpressions;
public partial class MyValidator
{
// ビルド時に正規表現のロジックが生成される
[GeneratedRegex(@"^[a-zA-Z0-9]+$")]
private static partial Regex AlphanumericRegex();
public bool IsAlphanumeric(string text)
{
// 生成されたメソッドを呼び出す
return AlphanumericRegex().IsMatch(text);
}
}
この方法の素晴らしい点は、正規表現のパターンにエラーがある場合に ビルドエラーとして検出できる ことです。
従来の文字列指定では実行してみるまで間違いに気づけませんでしたが、ソースジェネレーターは安全面でも非常に優れています。
実践的な判定パターンの例
よく使われる判定パターンの実装例を紹介します。
これらはそのままコピーして利用可能ですが、要件に応じて調整してください。
1. 数値のみの判定
「入力がすべて数字であること」を確認します。
// 先頭から末尾まで数字が1文字以上続く
string pattern = @"^\d+$";
bool isValid = Regex.IsMatch(input, pattern);
2. メールアドレス形式の簡易チェック
※厳密な RFC 準拠は非常に複雑なため、実用的な範囲でのチェックです。
string pattern = @"^[^@\s]+@[^@\s]+\.[^@\s]+$";
3. 郵便番号(日本)
「3桁-4桁」の形式をチェックします。
string pattern = @"^\d{3}-\d{4}$";
判定における注意点:アンカーの重要性
Regex.IsMatch を使用する際、多くの初心者が陥る罠が アンカー(^ と $)の付け忘れ です。
^:文字列の先頭を意味する$:文字列の末尾を意味する
例えば、@"\d{3}" というパターンで “abc123def” を判定すると、IsMatch は true を返します。
なぜなら、文字列の「どこか」に3桁の数字が含まれているからです。
もし「入力全体が3桁の数字であること」を保証したいなら、@”^\d{3}$” と記述しなければなりません。
パフォーマンスとセキュリティの懸念:ReDoS 回避
正規表現には ReDoS(Regular Expression Denial of Service) と呼ばれる脆弱性が存在します。
悪意のある巧妙に構成された文字列を複雑な正規表現に入力すると、処理時間が指数関数的に増大し、CPU を占有してしまう問題です。

対策:タイムアウトの設定
.NET の Regex クラスでは、処理の最大時間を制限する MatchTimeout を設定できます。
try
{
// 1秒以内に判定が終わらない場合は例外をスローする
TimeSpan timeout = TimeSpan.FromSeconds(1);
bool isMatch = Regex.IsMatch(input, pattern, RegexOptions.None, timeout);
}
catch (RegexMatchTimeoutException)
{
// タイムアウト発生時の処理
Console.WriteLine("判定処理がタイムアウトしました。");
}
ユーザー入力を直接正規表現にかけるWebアプリケーションなどでは、必ずタイムアウト値を設定する ことがベストプラクティスです。
よくある質問(FAQ)
- Q. Regex.IsMatch と String.Contains の違いは何ですか?
A. 柔軟性と速度の違いです。
単純な部分一致(例: “abc” が含まれるか)だけであれば、String.Contains の方が圧倒的に高速で、可読性も高いです。
「数字3桁が含まれるか」や「特定の形式に従っているか」といった複雑な条件の場合にのみ、Regex.IsMatch を使用しましょう。
- Q. 正規表現のパターンが正しいか確認するツールはありますか?
A. オンラインツールや拡張機能が便利です。
regex101 などの Web サイトを使えば、その場でパターンの一致テストや詳細な解説を確認できます。
また、Visual Studio にも正規表現のデバッグ機能が備わっています。
- Q. .NET 8 での変更点はありますか?
A. さらなる高速化が行われています。
.NET 7 以降、正規表現エンジンには大幅な最適化が入っており、特に非バックトラッキング型のエンジン(RegexOptions.NonBacktracking)の導入など、ReDoS 耐性が強化されています。
まとめ
C# の Regex.IsMatch は、文字列のパターン判定において非常に強力で便利なツールです。
その真価を発揮させるためのポイントを改めて整理します。
- 基本
逐語的リテラル
@""を使い、先頭^と末尾$のアンカーを意識する。- 効率
同じパターンを繰り返すならインスタンス化するか、C# 11以降なら
[GeneratedRegex]を活用する。- 安全
外部からの入力を処理する場合は、必ず MatchTimeout を設定して ReDoS 対策を行う。
- 選択
単純な文字列比較で済む場合は、無理に正規表現を使わず
ContainsやStartsWithを検討する。
正規表現を正しく使いこなすことで、バリデーションロジックはより堅牢で保守しやすいものになります。
最新の .NET の機能を積極的に取り入れ、高速で安全なアプリケーション開発を目指しましょう。
