C#を用いたアプリケーション開発において、ユーザーからの入力値が期待通りの形式であるかを確認する「バリデーション(入力検証)」は、システムの信頼性とセキュリティを担保するために不可欠な工程です。
その中でも、特定のパターンに合致するかを判定する正規表現(Regular Expression)によるチェックは、メールアドレスや電話番号、郵便番号といった定型データの検証に非常に強力な力を発揮します。
.NET環境では、System.Text.RegularExpressions名前空間に含まれるRegexクラスを使用することで、高度な正規表現操作をシンプルに実装することが可能です。
本記事では、C#で正規表現チェックを行うための基本となるRegex.IsMatchメソッドの使い方から、実務でそのまま使える正規表現パターン、さらには最新の.NETにおけるパフォーマンス最適化の手法まで、プロフェッショナルな視点で詳しく解説します。
C#における正規表現チェックの基本
C#で文字列が特定のパターンに一致するかどうかを判定する場合、最も一般的かつ簡便な方法がRegex.IsMatchメソッドを使用することです。
このメソッドは、対象の文字列が指定された正規表現にマッチすればtrueを、マッチしなければfalseを返します。
Regex.IsMatchの基本的な構文
Regex.IsMatchには、大きく分けて「静的メソッド」として呼び出す方法と、インスタンスを生成して呼び出す方法の2種類があります。
1. 静的メソッドによるチェック
一度きりのチェックや、頻繁に実行されないコードパスでは、静的メソッドを使用するのが手軽です。
using System;
using System.Text.RegularExpressions;
public class Program
{
public static void Main()
{
string input = "123-4567";
string pattern = @"^\d{3}-\d{4}$";
// 静的メソッドを使用してチェック
bool isValid = Regex.IsMatch(input, pattern);
Console.WriteLine($"入力値: {input}");
Console.WriteLine($"判定結果: {isValid}");
}
}
入力値: 123-4567
判定結果: True
2. インスタンスメソッドによるチェック
同じ正規表現パターンをループ内で繰り返し使用する場合や、パフォーマンスが求められる場面では、Regexクラスのインスタンスをあらかじめ生成しておく手法が推奨されます。
using System;
using System.Text.RegularExpressions;
public class Program
{
public static void Main()
{
string[] inputs = { "123-4567", "999-0000", "12-345" };
string pattern = @"^\d{3}-\d{4}$";
// Regexインスタンスを生成(コンパイルオプションを指定可能)
Regex regex = new Regex(pattern);
foreach (var input in inputs)
{
if (regex.IsMatch(input))
{
Console.WriteLine($"{input} : マッチしました");
}
else
{
Console.WriteLine($"{input} : 形式が正しくありません");
}
}
}
}
123-4567 : マッチしました
999-0000 : マッチしました
12-345 : 形式が正しくありません
正規表現で使用される主要なメタ文字
C#の正規表現で頻繁に利用されるメタ文字を整理します。
これらを組み合わせることで、複雑なバリデーションルールを構築できます。
| メタ文字 | 説明 | 例 |
|---|---|---|
^ | 文字列の先頭に一致 | ^A (Aから始まる) |
$ | 文字列の末尾に一致 | Z$ (Zで終わる) |
. | 任意の1文字に一致 | a.c (abc, a1cなど) |
\d | 任意の数字(0-9)に一致 | \d{3} (3桁の数字) |
\D | 数字以外の文字に一致 | \D |
\w | 英数字およびアンダースコアに一致 | \w+ |
\s | 空白文字(スペース、タブ等)に一致 | \s |
* | 直前の要素を0回以上繰り返す | a* |
+ | 直前の要素を1回以上繰り返す | a+ |
? | 直前の要素を0回または1回繰り返す | a? |
{n} | 直前の要素をちょうどn回繰り返す | \d{3} |
\[ \] | カッコ内のいずれかの文字に一致 | [A-Z] (英大文字) |
実践的な正規表現パターン集
実際のアプリケーション開発で頻用される具体的なパターン例を紹介します。
これらはコピー&ペーストでそのまま利用可能ですが、要件に応じて細部を調整してください。
1. メールアドレスの形式チェック
メールアドレスの厳密な正規表現は非常に複雑ですが、実務上は「基本的な構成(@マークの有無やドメイン形式)」をチェックするパターンが一般的です。
// 簡易的なメールアドレスチェック用パターン
string emailPattern = @"^[^@\s]+@[^@\s]+\.[^@\s]+$";
このパターンは、「@を含み、その前後に空白以外の文字があり、ドメイン部分にドットが含まれること」を確認します。
2. 電話番号(日本国内)のチェック
固定電話や携帯電話(ハイフンあり)の形式をチェックする例です。
// 日本の電話番号形式(例:090-1234-5678 または 03-1234-5678)
string telPattern = @"^0\d{1,4}-\d{1,4}-\d{4}$";
3. パスワードの強度チェック
「英大文字、英小文字、数字をそれぞれ1文字以上含み、8文字以上」という複雑な条件も、肯定先読み(Positive Lookahead)を使用することで1つの正規表現で表現できます。
// 強固なパスワード(英大文字・小文字・数字を含み8文字以上)
string passwordPattern = @"^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{8,}$";
実装例:複数のバリデーションを一括で行う
using System;
using System.Text.RegularExpressions;
public class Validator
{
public static void ValidateInput(string email, string phone)
{
// メールアドレスチェック
bool isEmailValid = Regex.IsMatch(email, @"^[^@\s]+@[^@\s]+\.[^@\s]+$");
// 電話番号チェック
bool isPhoneValid = Regex.IsMatch(phone, @"^0\d{1,4}-\d{1,4}-\d{4}$");
Console.WriteLine($"Email ({email}): {isEmailValid}");
Console.WriteLine($"Phone ({phone}): {isPhoneValid}");
}
}
正規表現のパフォーマンス最適化
正規表現は強力なツールですが、不適切な使用はCPU負荷の増大や、アプリケーションの応答停止(ReDoS攻撃への脆弱性)を招く恐れがあります。
C#では、パフォーマンスを最適化するための機能がいくつか用意されています。
RegexOptions.Compiled の利用
静的メソッドや通常のインスタンス生成では、実行時に正規表現のパターンが解析されます。
これに対し、RegexOptions.Compiledを指定すると、正規表現をMSIL(Microsoft Intermediate Language)に事前にコンパイルします。
// コンパイルオプションを指定してインスタンス化
var regex = new Regex(@"^\d{10}$", RegexOptions.Compiled);
メリット:
- 一度コンパイルされると、その後のマッチング処理が大幅に高速化されます。
デメリット:
- 初回のコンパイル時にコストがかかり、メモリ消費量も増加します。そのため、アプリケーション全体で何度も再利用されるパターンに限定して使用すべきです。
.NET 7以降の新機能:Source Generators
最新の.NET環境(.NET 7および.NET 8以降)では、Regex Source Generatorが導入されました。
これはコンパイル時に正規表現を解析し、最適なC#コードを自動生成する仕組みです。
実行時の解析コストがゼロになり、AOT(Ahead-Of-Time)コンパイルとも相性が良いため、現在最も推奨される手法です。
using System;
using System.Text.RegularExpressions;
public partial class MyValidator
{
// Source Generatorを利用した正規表現の定義
[GeneratedRegex(@"^\d{3}-\d{4}$")]
private static partial Regex ZipCodeRegex();
public static void Check(string input)
{
if (ZipCodeRegex().IsMatch(input))
{
Console.WriteLine("有効な郵便番号です。");
}
}
}
この方法を用いると、静的解析によって実行前にエラーを検知できるほか、実行速度が大幅に向上します。
正規表現チェックにおける注意点とセキュリティ
正規表現を扱う際には、単にマッチするかどうかだけでなく、安全性についても考慮する必要があります。
タイムアウトの設定
複雑な正規表現に対して、意図的にマッチングに時間がかかるような入力を与える攻撃を「ReDoS(Regular Expression Denial of Service)」と呼びます。
これを防ぐために、マッチング処理には必ずタイムアウトを設定してください。
try
{
// タイムアウトを1秒に設定
TimeSpan timeout = TimeSpan.FromSeconds(1);
bool isMatch = Regex.IsMatch(input, pattern, RegexOptions.None, timeout);
}
catch (RegexMatchTimeoutException)
{
// タイムアウト発生時の処理
Console.WriteLine("解析が規定時間を超えました。");
}
逐語的文字列リテラルの利用
C#で正規表現を記述する際は、文字列の前に @ を付ける「逐語的文字列リテラル(Verbatim String Literal)」を使用するのが一般的です。
これにより、バックスラッシュ(\)をエスケープ(\)せずに記述できるため、可読性が向上します。
- 通常:
"\\d{3}-\\d{4}" - 逐語的:
@"\d{3}-\d{4}"
入力の正規化
正規表現でチェックを行う前に、全角数字を半角に変換したり、前後の余計な空白を除去(Trim)したりする「正規化」を行うことで、バリデーションの精度を高めることができます。
string input = " 090-1234-5678 ";
string cleanInput = input.Trim(); // 前後の空白を除去
bool isValid = Regex.IsMatch(cleanInput, @"^0\d{1,4}-\d{1,4}-\d{4}$");
便利な正規表現チェックのデバッグ手法
複雑なパターンを構築する場合、コードを書いて実行して確認する作業は非効率です。
効率的な開発のために、以下の手法やツールを活用しましょう。
1. オンラインデバッガーの活用
「Regex101」などのWebサービスを使用すると、リアルタイムでマッチング結果やキャプチャグループの内容を確認できます。
C#(.NET)の正規表現エンジンを選択してテストできるため、非常に有用です。
2. Visual Studioのヒント機能
最新のVisual Studioでは、ソースコード上の正規表現文字列にマウスカーソルを合わせると、そのパターンの解説が表示されたり、簡単なテストを行えたりする機能が搭載されています。
まとめ
C#における正規表現チェックは、Regex.IsMatchメソッドを軸として、非常に柔軟かつ強力に実装することができます。
本記事で解説したポイントをまとめます。
- 基本: 簡易的なチェックには静的メソッド、繰り返しの利用にはインスタンスメソッドを選択する。
- 実践: メール、電話番号、パスワードなどの定型パターンを理解し、メタ文字を組み合わせてカスタマイズする。
- 最適化: .NET 7以降であれば
[GeneratedRegex]を活用し、コンパイル時生成による最高速のチェックを目指す。 - 安全: ReDoS対策として必ずタイムアウトを設定し、予期せぬ入力によるハングアップを防止する。
正規表現は一見難解に感じられますが、一度習得すればC#に限らず多くのプログラミング言語で応用が効く一生モノのスキルとなります。
まずは簡単なパターンから取り入れ、堅牢なアプリケーション開発に役立ててください。
