C#正規表現入門|Regexの使い方・判定・抽出・置換をサンプルで解説

はじめに

C#で文字列を扱う際、「数字だけで構成されているか判定したい」「文章からURLを抽出したい」「特定の文字列を別の形式に置換したい」といった処理が必要になることがあります。このような文字列処理を柔軟に実装できるのが正規表現です。

C#では、System.Text.RegularExpressions名前空間のRegexクラスを使って正規表現を扱います。Regex.IsMatchによる判定、Regex.MatchRegex.Matchesによる抽出、Regex.Replaceによる置換などが可能です。

この記事では、C#正規表現の基本的な使い方から、実務で利用できるサンプル、パフォーマンスや安全性の注意点まで解説します。

1. C#の正規表現とは?Regexでできること

1-1. 正規表現で文字列の判定・検索・抽出・置換ができる

正規表現とは、文字列のパターンを記号で表現する方法です。

たとえば、次の正規表現は「1文字以上の数字」を表します。

regex
\d+

C#のRegexクラスを利用すると、このようなパターンを使って次の処理を実行できます。

  • 文字列が指定した形式に一致するか判定する

  • 文字列内に目的のパターンが存在するか検索する

  • 一致した部分を抽出する

  • 一致した部分を別の文字列に置換する

  • 一致した部分を削除する

  • 指定したパターンを区切りとして文字列を分割する

単純な固定文字列だけでなく、「数字が3桁」「英字で始まる」「ハイフンの前後に数字がある」といった条件も表現できます。

1-2. C#で正規表現を使う場面

C#正規表現は、次のような場面で利用されます。

  • メールアドレスや電話番号の形式チェック

  • 郵便番号や会員番号の入力チェック

  • ログファイルから日時やエラーコードを抽出

  • HTMLやテキストからURLを抽出

  • 不要な空白や記号の削除

  • 日付や電話番号の表示形式を変換

  • 複数の区切り文字を使った文字列分割

  • 個人情報の一部を伏せ字にする

ただし、正規表現は文字列の「形式」を確認するものです。メールアドレスが実際に存在するか、電話番号が現在使われているかまでは判断できません。

1-3. Stringメソッドとの違い

C#には、正規表現を使わずに文字列を操作できるメソッドもあります。

C#
string text = "C# Regex Sample";

bool contains = text.Contains("Regex");
bool startsWith = text.StartsWith("C#");
string replaced = text.Replace("Sample", "Example");

固定文字列の検索や単純な置換であれば、String.ContainsString.Replaceのほうが読みやすく、処理も簡単です。

一方、次のような条件には正規表現が適しています。

3桁または4桁の数字
先頭が英字で、その後に英数字が続く文字列
複数の連続した空白
ハイフンを含む電話番号

処理内容に応じて、通常の文字列メソッドと正規表現を使い分けることが重要です。

1-4. この記事で学べること

この記事では、次の内容をサンプルコードとともに解説します。

  • Regex.IsMatchによる文字列の判定

  • Regex.MatchRegex.Matchesによる抽出

  • Regex.Replaceによる置換と削除

  • グループと名前付きグループの使い方

  • よく使う正規表現パターン

  • RegexOptionsによる動作の変更

  • タイムアウトやバックトラッキングへの対策

  • C#正規表現で発生しやすい問題の解決方法

2. C#で正規表現を使う準備

2-1. System.Text.RegularExpressionsを読み込む

C#で正規表現を使うには、System.Text.RegularExpressions名前空間を読み込みます。

C#
using System.Text.RegularExpressions;

これにより、RegexMatchMatchCollectionなどのクラスを簡潔に記述できます。

usingを追加しない場合は、完全修飾名でも呼び出せます。

C#
bool result = System.Text.RegularExpressions.Regex.IsMatch(
"12345",
@"^\d+$"
);

通常はファイルの先頭にusingを記述する方法がわかりやすいでしょう。

2-2. Regexクラスの基本構文

Regexクラスには、判定・抽出・置換などのメソッドが用意されています。

C#
using System.Text.RegularExpressions;

string input = "商品番号は12345です";
string pattern = @"\d+";

bool exists = Regex.IsMatch(input, pattern);
Match match = Regex.Match(input, pattern);
MatchCollection matches = Regex.Matches(input, pattern);
string result = Regex.Replace(input, pattern, "*****");

各メソッドの役割は次のとおりです。

メソッド用途
Regex.IsMatchパターンに一致する部分があるか判定する
Regex.Match最初に一致した結果を取得する
Regex.Matches一致したすべての結果を取得する
Regex.Replace一致した部分を置換する
Regex.Split一致した部分を区切りとして分割する

2-3. 静的メソッドとRegexインスタンスの違い

Regexには、静的メソッドとして使う方法と、インスタンスを生成して使う方法があります。

静的メソッドを使う例は次のとおりです。

C#
bool result = Regex.IsMatch("12345", @"^\d+$");

インスタンスを使う場合は、最初に正規表現を生成します。

C#
Regex numberRegex = new Regex(@"^\d+$");

bool result1 = numberRegex.IsMatch("12345");
bool result2 = numberRegex.IsMatch("ABC");

同じパターンを複数回利用する場合は、Regexインスタンスを保持すると、コードの重複を減らせます。

C#
private static readonly Regex PostalCodeRegex = new(
@"^\d{3}-\d{4}$",
RegexOptions.CultureInvariant,
TimeSpan.FromSeconds(1)
);

一度しか使わない簡単な処理では静的メソッド、繰り返し使うパターンではインスタンスを利用すると整理しやすくなります。

2-4. @を使った逐語的文字列リテラルの書き方

C#の文字列では、バックスラッシュ\がエスケープ文字として使われます。そのため、通常の文字列リテラルで正規表現の\dを記述するには、バックスラッシュを2つ重ねる必要があります。

C#
string pattern = "^\\d+$";

逐語的文字列リテラルを表す@を付けると、バックスラッシュをそのまま記述できます。

C#
string pattern = @"^\d+$";

正規表現ではバックスラッシュを頻繁に使うため、一般的には@を付けた書き方が読みやすくなります。

パスを含む正規表現でも違いが明確です。

C#
string normalPattern = "^C:\\\\temp\\\\.+$";
string verbatimPattern = @"^C:\\temp\\.+$";

ただし、逐語的文字列リテラル内でダブルクォートを表す場合は、""と2つ重ねます。

C#
string pattern = @"""[^""]*""";

3. Regex.IsMatchで文字列が正規表現に一致するか判定する

3-1. IsMatchの基本構文

Regex.IsMatchは、対象文字列に正規表現と一致する部分が存在するかをbool型で返します。

C#
bool result = Regex.IsMatch(input, pattern);

使用例は次のとおりです。

C#
using System;
using System.Text.RegularExpressions;

string input = "注文番号: 12345";
bool result = Regex.IsMatch(input, @"\d+");

Console.WriteLine(result); // True

inputの中に1文字以上の数字があるため、結果はtrueです。

3-2. 数字だけか判定するサンプル

文字列全体が数字だけで構成されているか判定するには、先頭を表す^と末尾を表す$を使います。

C#
using System.Text.RegularExpressions;

string input = "123456";
bool isNumber = Regex.IsMatch(input, @"^\d+$");

Console.WriteLine(isNumber); // True

このパターンは、次の要素で構成されています。

^    文字列の先頭
\d 数字
+ 直前のパターンが1回以上
$ 文字列の末尾

空文字も許可したい場合は、+ではなく*を使います。

C#
bool result = Regex.IsMatch("", @"^\d*$");

Console.WriteLine(result); // True

数値として変換できるかを確認することが目的なら、正規表現ではなくint.TryParsedecimal.TryParseも検討してください。

C#
bool isInteger = int.TryParse("123456", out int number);

3-3. メールアドレス形式を判定するサンプル

簡易的なメールアドレスの形式チェックは、次のように記述できます。

C#
using System.Text.RegularExpressions;

string email = "user@example.com";
string pattern = @"^[^@\s]+@[^@\s]+\.[^@\s]+$";

bool isValidFormat = Regex.IsMatch(email, pattern);

Console.WriteLine(isValidFormat); // True

このパターンでは、次の条件を確認しています。

  • @の前に、空白と@以外の文字が1文字以上ある

  • @が1つある

  • @の後に、空白と@以外の文字が1文字以上ある

  • ドットの後に、空白と@以外の文字が1文字以上ある

これは一般的な入力チェック向けの簡易パターンです。メールアドレスの厳密な仕様を正規表現だけで完全に表現しようとすると、パターンが非常に複雑になります。

また、形式が正しくても、そのメールアドレスが実在するとは限りません。実在確認が必要な場合は、確認メールの送信など別の仕組みが必要です。

3-4. 完全一致と部分一致の違い

Regex.IsMatchは、初期状態では文字列の一部が一致するだけでもtrueを返します。

C#
bool result = Regex.IsMatch("ABC123XYZ", @"\d+");

Console.WriteLine(result); // True

ABC123XYZの中に123があるため一致します。

一方、次のコードでは文字列全体が数字でなければ一致しません。

C#
bool result = Regex.IsMatch("ABC123XYZ", @"^\d+$");

Console.WriteLine(result); // False

入力値全体の形式を検証するときは、部分一致ではなく完全一致になっているか確認しましょう。

3-5. ^と$を使って先頭・末尾を指定する

^は先頭、$は末尾を表します。

英字で始まるか判定する例は次のとおりです。

C#
bool result = Regex.IsMatch("User123", @"^[A-Za-z]");

Console.WriteLine(result); // True

数字で終わるか判定する例です。

C#
bool result = Regex.IsMatch("User123", @"\d$");

Console.WriteLine(result); // True

文字列全体が4桁の数字か判定する場合は、両方を使います。

C#
bool result = Regex.IsMatch("2026", @"^\d{4}$");

Console.WriteLine(result); // True

複数行モードでは^$の意味が変化する点にも注意が必要です。文字列全体の絶対的な先頭と末尾を指定したい場合は、\A\zも利用できます。

C#
bool result = Regex.IsMatch("12345", @"\A\d+\z");

4. Regex.Match・Matchesで一致した文字列を抽出する

4-1. Matchで最初に一致した文字列を取得する

Regex.Matchは、最初に一致した結果をMatchオブジェクトとして返します。

C#
using System;
using System.Text.RegularExpressions;

string input = "商品Aは1200円、商品Bは2500円です";
Match match = Regex.Match(input, @"\d+");

if (match.Success)
{
Console.WriteLine(match.Value); // 1200
}

一致したかどうかは、Match.Successで確認します。一致しなかった場合、Regex.Matchnullを返すわけではありません。

C#
Match match = Regex.Match("ABC", @"\d+");

Console.WriteLine(match.Success); // False
Console.WriteLine(match.Value); // 空文字

4-2. Matchesで複数の一致結果を取得する

Regex.Matchesは、一致したすべての結果をMatchCollectionとして返します。

C#
using System;
using System.Text.RegularExpressions;

string input = "商品Aは1200円、商品Bは2500円です";
MatchCollection matches = Regex.Matches(input, @"\d+");

foreach (Match match in matches)
{
Console.WriteLine(match.Value);
}

実行結果は次のとおりです。

1200
2500

一致件数はCountプロパティで取得できます。

C#
Console.WriteLine(matches.Count); // 2

4-3. Value・Index・Lengthの使い方

Matchオブジェクトには、一致結果の内容や位置を確認するプロパティがあります。

C#
string input = "価格は1500円です";
Match match = Regex.Match(input, @"\d+");

if (match.Success)
{
Console.WriteLine(match.Value); // 1500
Console.WriteLine(match.Index); // 一致開始位置
Console.WriteLine(match.Length); // 4
}

主なプロパティは次のとおりです。

プロパティ内容
Success一致したかどうか
Value一致した文字列
Index一致が始まった位置
Length一致した文字数
Groupsグループごとの一致結果

一致位置を使って、前後の文字列を取得することもできます。

C#
string before = input[..match.Index];
string after = input[(match.Index + match.Length)..];

Console.WriteLine(before); // 価格は
Console.WriteLine(after); // 円です

4-4. Groupを使って一部だけ抽出する

丸括弧()で囲んだ部分は、グループとして取得できます。

次の例では、日付から年・月・日を個別に抽出します。

C#
using System;
using System.Text.RegularExpressions;

string input = "2026-06-20";
string pattern = @"^(\d{4})-(\d{2})-(\d{2})$";

Match match = Regex.Match(input, pattern);

if (match.Success)
{
Console.WriteLine(match.Groups[0].Value); // 2026-06-20
Console.WriteLine(match.Groups[1].Value); // 2026
Console.WriteLine(match.Groups[2].Value); // 06
Console.WriteLine(match.Groups[3].Value); // 20
}

Groups[0]には正規表現全体の一致結果が入ります。個別のグループはGroups[1]以降です。

抽出する必要がないグループには、非キャプチャグループ(?:...)を使えます。

C#
string pattern = @"(?:https?://)?([^/\s]+)";

4-5. 名前付きグループで抽出結果をわかりやすくする

グループ番号だけでは、正規表現が複雑になったときに意味がわかりにくくなります。名前付きグループを使うと、用途を明確にできます。

C#では、(?<名前>パターン)の形式で名前を指定します。

C#
using System;
using System.Text.RegularExpressions;

string input = "2026-06-20";
string pattern =
@"^(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})$";

Match match = Regex.Match(input, pattern);

if (match.Success)
{
string year = match.Groups["year"].Value;
string month = match.Groups["month"].Value;
string day = match.Groups["day"].Value;

Console.WriteLine($"年: {year}");
Console.WriteLine($"月: {month}");
Console.WriteLine($"日: {day}");
}

ログ解析など複数の項目を抽出する処理では、名前付きグループを使うと保守しやすくなります。

5. Regex.Replaceで文字列を置換・削除する

5-1. Replaceの基本構文

Regex.Replaceは、正規表現に一致した部分を指定した文字列に置換します。

C#
string result = Regex.Replace(input, pattern, replacement);

空白が連続している部分を1つの空白にまとめる例です。

C#
using System.Text.RegularExpressions;

string input = "C# Regex Sample";
string result = Regex.Replace(input, @"\s+", " ");

Console.WriteLine(result); // C# Regex Sample

元の文字列は変更されず、置換後の新しい文字列が返されます。

5-2. 数字を伏せ字に置換するサンプル

文章内の数字をアスタリスクに置換する例です。

C#
string input = "カード番号は1234-5678です";
string result = Regex.Replace(input, @"\d", "*");

Console.WriteLine(result); // カード番号は****-****です

数字のまとまりを1つの固定文字列に置換することもできます。

C#
string input = "注文番号12345を処理しました";
string result = Regex.Replace(input, @"\d+", "[番号]");

Console.WriteLine(result);
// 注文番号[番号]を処理しました

一致した文字数と同じ数の*に置換したい場合は、MatchEvaluatorを利用します。

C#
string result = Regex.Replace(
input,
@"\d+",
match => new string('*', match.Length)
);

5-3. 空文字に置換して不要な文字を削除する

置換後の文字列に空文字""を指定すると、一致した部分を削除できます。

C#
string input = "商品コード: AB-123-XY";
string result = Regex.Replace(input, @"[-: ]", "");

Console.WriteLine(result); // 商品コードAB123XY

数字以外をすべて削除する例です。

C#
string input = "電話: 03-1234-5678";
string digits = Regex.Replace(input, @"\D", "");

Console.WriteLine(digits); // 0312345678

\Dは「数字以外の1文字」を表します。

5-4. グループを使って日付形式を変換する

置換文字列では、$1$2のようにグループの一致結果を参照できます。

C#
using System.Text.RegularExpressions;

string input = "2026-06-20";
string pattern = @"^(\d{4})-(\d{2})-(\d{2})$";
string replacement = "$1年$2月$3日";

string result = Regex.Replace(input, pattern, replacement);

Console.WriteLine(result); // 2026年06月20日

名前付きグループは、${名前}で参照できます。

C#
string pattern =
@"^(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})$";

string replacement = "${year}年${month}月${day}日";

string result = Regex.Replace(input, pattern, replacement);

置換後の文字列にドル記号そのものを含める場合は、$$と記述します。

5-5. MatchEvaluatorで置換処理をカスタマイズする

MatchEvaluatorを使うと、一致結果ごとにC#の処理を実行して置換文字列を決定できます。

数字を2倍にする例です。

C#
using System;
using System.Text.RegularExpressions;

string input = "商品Aは100円、商品Bは250円です";

string result = Regex.Replace(
input,
@"\d+",
match =>
{
int value = int.Parse(match.Value);
return (value * 2).ToString();
}
);

Console.WriteLine(result);
// 商品Aは200円、商品Bは500円です

単語の先頭を大文字にする例です。

C#
string input = "csharp regex sample";

string result = Regex.Replace(
input,
@"\b[a-z]",
match => match.Value.ToUpperInvariant()
);

Console.WriteLine(result); // Csharp Regex Sample

単純な置換文字列では対応できない計算や条件分岐が必要な場合に便利です。

6. C#正規表現でよく使うパターン一覧

6-1. 数字を表す\d

\dは数字1文字を表します。

C#
Regex.IsMatch("A1", @"\d");       // True
Regex.IsMatch("123", @"^\d+$"); // True

回数を指定する場合は量指定子を組み合わせます。

regex
\d{4}

これは数字4文字を表します。

C#
bool result = Regex.IsMatch("2026", @"^\d{4}$");

\dはUnicode上の数字にも一致します。ASCIIの0から9だけに限定したい場合は、[0-9]を使うと意図が明確です。

C#
bool result = Regex.IsMatch("1234", @"^[0-9]{4}$");

数字以外は、大文字の\Dで表します。

regex
\D

6-2. 英数字・単語文字を表す\w

\wは単語を構成する文字に一致します。C#の正規表現では、ASCIIの英数字だけでなく、Unicodeカテゴリに基づく文字やアンダースコアなども対象になります。

C#
bool result = Regex.IsMatch("User_01", @"^\w+$");

ASCIIの英数字とアンダースコアだけに限定したい場合は、文字クラスを明示します。

C#
bool result = Regex.IsMatch(
"User_01",
@"^[A-Za-z0-9_]+$"
);

単語文字以外は\Wで表します。

6-3. 空白文字を表す\s

\sは、スペースやタブ、改行などの空白文字に一致します。

C#
string input = "C#\tRegex\nSample";
string result = Regex.Replace(input, @"\s+", " ");

Console.WriteLine(result); // C# Regex Sample

空白以外は\Sで表します。

文字列の前後にある空白だけを削除するなら、正規表現ではなくTrimも利用できます。

C#
string result = input.Trim();

6-4. 任意の1文字を表す.

.は、原則として改行以外の任意の1文字に一致します。

C#
bool result = Regex.IsMatch("cat", @"c.t");

Console.WriteLine(result); // True

catcutc1tなどに一致します。

ドットそのものに一致させたい場合は、\.とエスケープします。

C#
bool result = Regex.IsMatch("example.com", @"example\.com");

.に改行も含めたい場合は、RegexOptions.Singlelineを指定します。

6-5. 0回以上・1回以上・任意回数を表す量指定子

量指定子は、直前のパターンが何回繰り返されるかを指定します。

量指定子意味
*0回以上
+1回以上
?0回または1回
{n}ちょうどn回
{n,}n回以上
{n,m}n回以上m回以下

使用例は次のとおりです。

regex
\d*       数字が0文字以上
\d+ 数字が1文字以上
-? ハイフンが0個または1個
\d{4} 数字が4文字
\d{2,4} 数字が2文字以上4文字以下

C#での例です。

C#
bool result = Regex.IsMatch("123", @"^\d{2,4}$");

Console.WriteLine(result); // True

6-6. 文字クラス[]と否定[^]の使い方

角括弧[]は、指定した文字のいずれか1文字を表します。

regex
[ABC]

これはABCのいずれかに一致します。

範囲も指定できます。

regex
[A-Z]
[a-z]
[0-9]

英数字だけの文字列を判定する例です。

C#
bool result = Regex.IsMatch(
"User2026",
@"^[A-Za-z0-9]+$"
);

文字クラスの先頭に^を置くと否定になります。

regex
[^0-9]

これは数字以外の1文字に一致します。

C#
string digits = Regex.Replace(
"TEL: 03-1234-5678",
@"[^0-9]",
""
);

Console.WriteLine(digits); // 0312345678

文字クラス外の^は文字列の先頭を表すため、位置によって意味が異なります。

6-7. OR条件を表す|の使い方

縦線|はOR条件を表します。

regex
cat|dog

これはcatまたはdogに一致します。

C#
bool result = Regex.IsMatch(
"I have a dog.",
@"cat|dog"
);

Console.WriteLine(result); // True

範囲を明確にするには、グループと組み合わせます。

regex
^(cat|dog)$

抽出結果が不要なら、非キャプチャグループを使えます。

regex
^(?:cat|dog)$

電話番号の市外局番候補を表す例です。

regex
^(?:03|06)-\d{4}-\d{4}$

6-8. エスケープが必要な特殊文字

正規表現では、次の文字が特別な意味を持ちます。

. ^ $ * + ? ( ) [ ] { } \ |

これらの文字そのものに一致させるには、バックスラッシュでエスケープします。

regex
\.
\+
\?
\(
\)

たとえば、ドット区切りのバージョン番号を判定する場合は次のように書きます。

C#
bool result = Regex.IsMatch(
"1.2.3",
@"^\d+\.\d+\.\d+$"
);

外部から受け取った文字列を正規表現の固定文字列として利用するときは、Regex.Escapeを使います。

C#
string keyword = "C# (入門)";
string escaped = Regex.Escape(keyword);

bool result = Regex.IsMatch(
"これはC# (入門)の記事です",
escaped
);

7. 実務で使えるC#正規表現サンプル集

7-1. 電話番号を判定する

ハイフンで区切られた日本の電話番号を簡易的に判定する例です。

C#
using System.Text.RegularExpressions;

string phone = "03-1234-5678";
string pattern = @"^0\d{1,4}-\d{1,4}-\d{4}$";

bool isValidFormat = Regex.IsMatch(phone, pattern);

Console.WriteLine(isValidFormat); // True

携帯電話番号を簡易判定する例です。

C#
string mobile = "090-1234-5678";
string pattern = @"^0[789]0-\d{4}-\d{4}$";

bool result = Regex.IsMatch(mobile, pattern);

電話番号にはさまざまな桁数や区切り方があるため、対象とする番号体系を明確にしてからパターンを設計する必要があります。

正規化が目的なら、先に数字以外を削除する方法もあります。

C#
string normalized = Regex.Replace(
"090-1234-5678",
@"\D",
""
);

Console.WriteLine(normalized); // 09012345678

7-2. 郵便番号を判定する

日本の郵便番号形式123-4567を判定する例です。

C#
string postalCode = "123-4567";
string pattern = @"^\d{3}-\d{4}$";

bool result = Regex.IsMatch(postalCode, pattern);

Console.WriteLine(result); // True

ハイフンを省略した形式も許可する場合は、-?を使います。

C#
string pattern = @"^\d{3}-?\d{4}$";

Console.WriteLine(Regex.IsMatch("123-4567", pattern)); // True
Console.WriteLine(Regex.IsMatch("1234567", pattern)); // True

正規表現で確認できるのは桁数と形式です。実在する郵便番号かどうかは、郵便番号データとの照合が必要です。

7-3. URLを抽出する

文章からHTTPまたはHTTPSのURLを抽出する簡易例です。

C#
using System;
using System.Text.RegularExpressions;

string input =
"公式サイトはhttps://example.com、" +
"資料はhttp://example.net/docsです。";

string pattern = @"https?://[^\s、。]+";

foreach (Match match in Regex.Matches(input, pattern))
{
Console.WriteLine(match.Value);
}

実行結果は次のようになります。

https://example.com
http://example.net/docs

URLの仕様は複雑であり、末尾の括弧や句読点などを正確に扱うには追加の調整が必要です。URLとして利用可能か確認したい場合は、抽出後にUri.TryCreateを使う方法があります。

C#
if (Uri.TryCreate(
match.Value,
UriKind.Absolute,
out Uri? uri))
{
Console.WriteLine(uri);
}

7-4. HTMLタグを削除する

簡単なHTML文字列からタグらしき部分を削除する例です。

C#
string html = "<p>C#の<strong>正規表現</strong>を学ぶ</p>";
string text = Regex.Replace(html, @"<[^>]+>", "");

Console.WriteLine(text);
// C#の正規表現を学ぶ

ただし、HTMLは入れ子構造、属性、コメント、スクリプトなどを含むため、一般的なHTML解析を正規表現だけで安全に行うことは困難です。

複雑なHTMLから要素や属性を取得する場合は、HTMLパーサーの利用を検討してください。また、タグを削除しても、スクリプトなどの危険な内容を安全に無害化できるとは限りません。

7-5. CSVやカンマ区切り文字列を分割する

単純なカンマ区切り文字列は、String.Splitで分割できます。

C#
string input = "Apple,Banana,Orange";
string[] values = input.Split(',');

カンマの前後に任意の空白がある場合は、Regex.Splitが便利です。

C#
using System.Text.RegularExpressions;

string input = "Apple, Banana ,Orange";
string[] values = Regex.Split(input, @"\s*,\s*");

foreach (string value in values)
{
Console.WriteLine(value);
}

複数の区切り文字に対応することもできます。

C#
string input = "Apple, Banana; Orange";
string[] values = Regex.Split(input, @"\s*[,;]\s*");

ただし、正式なCSVでは、ダブルクォート内のカンマや改行、エスケープされたダブルクォートを扱う必要があります。

csv
"Tokyo, Japan","Sample ""Name""",100

このようなCSVを確実に処理する場合は、CSVパーサーを利用するほうが安全です。

7-6. 全角・半角や日本語を含む文字列を扱う

ひらがなだけで構成されているか判定する簡易例です。

C#
bool result = Regex.IsMatch(
"こんにちは",
@"^[ぁ-ん]+$"
);

カタカナを判定する例です。

C#
bool result = Regex.IsMatch(
"サンプル",
@"^[ァ-ヶー]+$"
);

漢字を含むか調べる例です。

C#
bool containsKanji = Regex.IsMatch(
"東京都",
@"\p{IsCJKUnifiedIdeographs}"
);

Unicodeカテゴリを使って文字を判定することもできます。

C#
bool containsLetter = Regex.IsMatch("東京2026", @"\p{L}");
bool containsNumber = Regex.IsMatch("東京2026", @"\p{N}");

全角数字を半角数字へ変換するなどの正規化は、正規表現だけでなくstring.Normalizeや文字変換処理との組み合わせが必要になる場合があります。

C#
using System.Text;

string normalized = input.Normalize(NormalizationForm.FormKC);

正規化によって変換される文字の範囲は要件に合わせて確認してください。

7-7. ログ文字列から日時やエラーコードを抽出する

次のログから日時、ログレベル、エラーコード、メッセージを抽出します。

2026-06-20 10:15:30 ERROR E1024 Connection failed

名前付きグループを使うと、各項目をわかりやすく取得できます。

C#
using System;
using System.Text.RegularExpressions;

string log =
"2026-06-20 10:15:30 ERROR E1024 Connection failed";

string pattern =
@"^(?<date>\d{4}-\d{2}-\d{2})\s+" +
@"(?<time>\d{2}:\d{2}:\d{2})\s+" +
@"(?<level>INFO|WARN|ERROR)\s+" +
@"(?<code>[A-Z]\d{4})\s+" +
@"(?<message>.+)$";

Match match = Regex.Match(log, pattern);

if (match.Success)
{
Console.WriteLine(match.Groups["date"].Value);
Console.WriteLine(match.Groups["time"].Value);
Console.WriteLine(match.Groups["level"].Value);
Console.WriteLine(match.Groups["code"].Value);
Console.WriteLine(match.Groups["message"].Value);
}

日時として処理する場合は、抽出後にDateTime.TryParseExactなどで変換すると、存在しない日付も検出できます。

C#
using System.Globalization;

string dateTimeText =
$"{match.Groups["date"].Value} " +
$"{match.Groups["time"].Value}";

bool parsed = DateTime.TryParseExact(
dateTimeText,
"yyyy-MM-dd HH:mm:ss",
CultureInfo.InvariantCulture,
DateTimeStyles.None,
out DateTime timestamp
);

8. RegexOptionsで正規表現の動作を変更する

8-1. IgnoreCaseで大文字・小文字を区別しない

RegexOptions.IgnoreCaseを指定すると、英字などの大文字・小文字を区別せずに一致させられます。

C#
using System.Text.RegularExpressions;

bool result = Regex.IsMatch(
"CSharp",
@"csharp",
RegexOptions.IgnoreCase
);

Console.WriteLine(result); // True

インスタンス作成時にも指定できます。

C#
Regex regex = new Regex(
@"csharp",
RegexOptions.IgnoreCase
);

bool result = regex.IsMatch("CSHARP");

8-2. Multilineで複数行の先頭・末尾を扱う

通常、^$は入力文字列全体の先頭と末尾に対応します。

RegexOptions.Multilineを指定すると、各行の先頭と末尾にも対応します。

C#
string input =
"INFO Start\n" +
"ERROR Failed\n" +
"INFO End";

MatchCollection matches = Regex.Matches(
input,
@"^ERROR.*$",
RegexOptions.Multiline
);

foreach (Match match in matches)
{
Console.WriteLine(match.Value);
}

実行結果は次のとおりです。

ERROR Failed

Multilineを指定しても、.が改行に一致するようになるわけではありません。

8-3. Singlelineで.に改行を含める

RegexOptions.Singlelineを指定すると、.が改行を含む任意の1文字に一致します。

C#
string input = "<body>\nC# Regex\nSample\n</body>";

Match match = Regex.Match(
input,
@"<body>(.*?)</body>",
RegexOptions.Singleline
);

if (match.Success)
{
Console.WriteLine(match.Groups[1].Value);
}

オプションを指定せず、パターン内で改行を含む任意の文字を表したい場合は、[\s\S]を使う方法もあります。

regex
<body>([\s\S]*?)</body>

ただし、意図が明確になるようにSinglelineを使うほうが読みやすい場合があります。

8-4. Compiledでパフォーマンスを改善する

RegexOptions.Compiledを指定すると、正規表現を実行用コードへコンパイルして利用できます。

C#
private static readonly Regex NumberRegex = new(
@"^\d+$",
RegexOptions.Compiled,
TimeSpan.FromSeconds(1)
);

同じ正規表現を大量に繰り返す処理では、実行速度が改善する可能性があります。一方で、初期化時のコストやメモリ使用量が増えるため、常に高速になるとは限りません。

一度しか実行しない正規表現や、アプリケーション起動時に多数生成される正規表現では、かえって不利になる場合があります。実際の処理時間を測定したうえで利用を判断しましょう。

8-5. CultureInvariantの使いどころ

大文字・小文字の比較には、カルチャによって結果が変化する場合があります。

RegexOptions.CultureInvariantを指定すると、現在のカルチャに依存しない比較が行われます。

C#
Regex regex = new Regex(
@"^[a-z]+$",
RegexOptions.IgnoreCase |
RegexOptions.CultureInvariant,
TimeSpan.FromSeconds(1)
);

識別子、プロトコル、ファイル形式など、言語環境に関係なく同じルールで処理したい場合に適しています。

ユーザー向けの自然言語を扱う場合は、カルチャを無視することが要件に合わない場合もあります。

9. C#正規表現でつまずきやすいポイント

9-1. バックスラッシュのエスケープでエラーになる

C#の文字列と正規表現の両方で、バックスラッシュには特別な意味があります。

次の書き方は、C#の文字列リテラルとして正しくありません。

C#
string pattern = "\d+";

通常の文字列を使うなら、バックスラッシュを重ねます。

C#
string pattern = "\\d+";

逐語的文字列リテラルを使えば、次のように書けます。

C#
string pattern = @"\d+";

C#正規表現では、基本的に@"..."を使うとエスケープの混乱を減らせます。

9-2. GreedyとLazyの違いを理解する

量指定子*+は、初期状態では可能な限り長く一致します。これをGreedy、つまり貪欲な一致と呼びます。

C#
string input = "<b>One</b><b>Two</b>";
Match match = Regex.Match(input, @"<b>.*</b>");

Console.WriteLine(match.Value);
// <b>One</b><b>Two</b>

最初の<b>から最後の</b>まで一致しています。

量指定子の後ろに?を付けると、可能な限り短く一致するLazyになります。

C#
MatchCollection matches = Regex.Matches(
input,
@"<b>.*?</b>"
);

foreach (Match item in matches)
{
Console.WriteLine(item.Value);
}

実行結果は次のとおりです。

<b>One</b>
<b>Two</b>

ただし、Lazyにすれば常に安全または高速になるわけではありません。対象文字列の構造に応じて、より限定的な文字クラスを使う方法も検討してください。

regex
<b>[^<]*</b>

9-3. Matchが失敗したときの判定方法

Regex.Matchが一致に失敗しても、戻り値は通常nullではありません。Successプロパティで判定します。

C#
Match match = Regex.Match("ABC", @"\d+");

if (!match.Success)
{
Console.WriteLine("数字は見つかりませんでした");
}

次のようにValueだけを確認すると、一致した空文字との区別が難しくなることがあります。

C#
if (match.Value == "")
{
// 一致失敗とは限らない
}

たとえば、a*aが0文字でも一致できるため、空文字への一致が発生します。成功判定にはSuccessを利用しましょう。

9-4. 日本語や改行を含む文字列で期待通りに一致しない

\wを英数字だけだと思って使うと、日本語にも一致して予想外の結果になる場合があります。

ASCII英数字だけに限定するなら、明示的な文字クラスを使います。

C#
string pattern = @"^[A-Za-z0-9_]+$";

また、.は初期状態では改行に一致しません。

C#
string input = "開始\n終了";
bool result = Regex.IsMatch(input, @"開始.*終了");

Console.WriteLine(result); // False

改行を含める場合はSinglelineを指定します。

C#
bool result = Regex.IsMatch(
input,
@"開始.*終了",
RegexOptions.Singleline
);

入力に\r\n\nが混在する可能性がある場合は、改行パターンを次のように記述できます。

regex
\r?\n

9-5. 正規表現が複雑になりすぎる問題

正規表現に多くの条件を詰め込むと、読みづらく修正しにくいコードになります。

たとえば、入力チェックでは次のように役割を分けられます。

C#
if (string.IsNullOrWhiteSpace(input))
{
return false;
}

if (input.Length > 50)
{
return false;
}

return Regex.IsMatch(input, @"^[A-Za-z0-9_-]+$");

1つの巨大な正規表現にまとめるのではなく、次のように分割することが重要です。

  • 空文字チェックはString.IsNullOrWhiteSpace

  • 長さチェックはLength

  • 数値変換はTryParse

  • 日付の妥当性はDateTime.TryParseExact

  • 複雑な構造の解析は専用パーサー

  • 正規表現は文字の並び方の判定や抽出

複雑なパターンにはコメントを付けたり、RegexOptions.IgnorePatternWhitespaceで整形したりする方法もあります。

C#
string pattern = """
^
(?<year>\d{4}) #
-
(?<month>\d{2}) #
-
(?<day>\d{2}) #
$
""";

Regex regex = new Regex(
pattern,
RegexOptions.IgnorePatternWhitespace
);

10. C#正規表現のパフォーマンスと安全な使い方

10-1. Regexを使い回して無駄な生成を避ける

同じパターンを繰り返し利用する場合は、Regexインスタンスをフィールドとして保持できます。

C#
private static readonly Regex UserIdRegex = new(
@"^[A-Za-z][A-Za-z0-9_]{2,19}$",
RegexOptions.CultureInvariant,
TimeSpan.FromSeconds(1)
);

public static bool IsValidUserId(string input)
{
return UserIdRegex.IsMatch(input);
}

メソッドを呼び出すたびに新しいRegexを生成するコードは避けます。

C#
public static bool IsValidUserId(string input)
{
Regex regex = new Regex(@"^[A-Za-z][A-Za-z0-9_]{2,19}$");
return regex.IsMatch(input);
}

ただし、静的メソッドには内部キャッシュがあるため、単純にすべての呼び出しをインスタンス化すればよいわけではありません。利用頻度、パターン数、可読性を考慮して設計します。

10-2. タイムアウトを指定して処理停止を防ぐ

正規表現によっては、入力内容に応じて処理時間が急激に増えることがあります。外部入力を処理する場合は、タイムアウトを指定すると安全性を高められます。

C#
using System;
using System.Text.RegularExpressions;

try
{
bool result = Regex.IsMatch(
input,
pattern,
RegexOptions.None,
TimeSpan.FromSeconds(1)
);
}
catch (RegexMatchTimeoutException)
{
Console.WriteLine(
"正規表現の処理がタイムアウトしました"
);
}

インスタンスを生成するときにも指定できます。

C#
Regex regex = new Regex(
pattern,
RegexOptions.CultureInvariant,
TimeSpan.FromMilliseconds(500)
);

適切なタイムアウト時間は、入力サイズやシステム要件に応じて決めます。

10-3. バックトラッキングによる遅延に注意する

正規表現エンジンは、一致に失敗した際に別の組み合わせを試すことがあります。この処理をバックトラッキングと呼びます。

次のように量指定子が入れ子になったパターンは、入力によって大量の試行が発生する可能性があります。

regex
^(a+)+$

特に、末尾だけ一致しない長い文字列では問題になりやすくなります。

aaaaaaaaaaaaaaaaaaaaaaaaaaaaX

対策として、次の方法があります。

  • 不要な量指定子の入れ子を避ける

  • .*より限定的な文字クラスを使う

  • 入力文字列の最大長を制限する

  • 正規表現にタイムアウトを指定する

  • 単純な文字列処理へ置き換える

  • バックトラッキングが不要な設計を検討する

たとえば、行末までの数字を取得するなら、曖昧なパターンより具体的なパターンを使います。

regex
[0-9]+

10-4. ユーザー入力を正規表現に使うときの注意点

ユーザーが入力した検索語を、そのまま正規表現の一部へ連結すると、記号が正規表現として解釈されます。

C#
string keyword = userInput;
string pattern = $".*{keyword}.*";

ユーザーが([を入力すると、構文エラーになる可能性があります。また、意図しないパターンや処理負荷の高いパターンになる危険もあります。

固定文字列として検索したい場合は、Regex.Escapeを使います。

C#
string keyword = Regex.Escape(userInput);
string pattern = $".*{keyword}.*";

単純に部分一致を調べるだけなら、正規表現を使わずString.Containsを使うほうが適切です。

C#
bool contains = input.Contains(
userInput,
StringComparison.OrdinalIgnoreCase
);

ユーザー自身に正規表現を入力させる機能では、入力長の制限、タイムアウト、例外処理が重要です。

C#
try
{
Regex regex = new Regex(
userPattern,
RegexOptions.None,
TimeSpan.FromMilliseconds(500)
);

bool result = regex.IsMatch(input);
}
catch (ArgumentException)
{
Console.WriteLine("正規表現の形式が不正です");
}
catch (RegexMatchTimeoutException)
{
Console.WriteLine("処理がタイムアウトしました");
}

10-5. 読みやすい正規表現を書くコツ

正規表現は短く書くことより、後から理解できることが重要です。

読みやすくするために、次の点を意識します。

  • パターンを意味のある変数名や定数にする

  • 名前付きグループを利用する

  • 不要なキャプチャには(?:...)を使う

  • 入力全体を検証する場合は境界を明示する

  • ASCII限定かUnicode対応かを明確にする

  • 複雑な処理は複数段階に分ける

  • タイムアウトを指定する

  • 正常系と異常系のテストを用意する

たとえば、パターンを直接埋め込むより、名前を付けたほうが目的を理解しやすくなります。

C#
private const string PostalCodePattern =
@"^\d{3}-\d{4}$";

bool result = Regex.IsMatch(
input,
PostalCodePattern
);

入力例だけでなく、境界値や不正値もテストします。

C#
string[] validValues =
{
"123-4567"
};

string[] invalidValues =
{
"",
"12-34567",
"1234567",
"ABC-DEFG",
"123-45678"
};

11. C#正規表現に関するよくある質問

11-1. C#で正規表現を使うには何をusingすればいい?

System.Text.RegularExpressionsを読み込みます。

C#
using System.Text.RegularExpressions;

その後、Regexクラスを使って判定、抽出、置換などを実行できます。

C#
bool result = Regex.IsMatch(
"12345",
@"^\d+$"
);

11-2. Regex.MatchとRegex.Matchesの違いは?

Regex.Matchは最初に一致した1件を取得し、Regex.Matchesは一致したすべての結果を取得します。

C#
string input = "100円、200円、300円";

Match first = Regex.Match(input, @"\d+");
MatchCollection all = Regex.Matches(input, @"\d+");

Console.WriteLine(first.Value); // 100
Console.WriteLine(all.Count); // 3

最初の1件だけ必要ならMatch、すべての一致結果が必要ならMatchesを使います。

11-3. Regex.IsMatchで完全一致させるには?

パターンの先頭に^、末尾に$を付けます。

C#
bool result = Regex.IsMatch(
"12345",
@"^\d+$"
);

文字列全体の絶対的な先頭と末尾を指定したい場合は、\A\zも使えます。

C#
bool result = Regex.IsMatch(
"12345",
@"\A\d+\z"
);

特にRegexOptions.Multilineの影響を受けたくない場合は、\A\zが明確です。

11-4. 正規表現で改行を含めて検索するには?

.に改行を含めたい場合は、RegexOptions.Singlelineを指定します。

C#
Match match = Regex.Match(
input,
@"開始.*終了",
RegexOptions.Singleline
);

パターン内でオプションを指定することもできます。

C#
Match match = Regex.Match(
input,
@"(?s)開始.*終了"
);

改行コードそのものに一致させたい場合は、\r?\nなどを使います。

C#
string[] lines = Regex.Split(
input,
@"\r?\n"
);

11-5. 正規表現で置換後の文字列にマッチ結果を使うには?

番号付きグループは$1$2、名前付きグループは${name}で参照できます。

C#
string input = "2026-06-20";

string result = Regex.Replace(
input,
@"(\d{4})-(\d{2})-(\d{2})",
"$1年$2月$3日"
);

Console.WriteLine(result);
// 2026年06月20日

名前付きグループを使う例です。

C#
string result = Regex.Replace(
input,
@"(?<y>\d{4})-(?<m>\d{2})-(?<d>\d{2})",
"${y}年${m}月${d}日"
);

置換内容をC#の処理で決めたい場合は、MatchEvaluatorを使います。

C#
string result = Regex.Replace(
"10 20 30",
@"\d+",
match =>
(int.Parse(match.Value) + 1).ToString()
);

11-6. RegexとString.Containsはどちらを使うべき?

固定文字列が含まれているか調べるだけなら、String.Containsが適しています。

C#
bool result = input.Contains(
"Regex",
StringComparison.OrdinalIgnoreCase
);

数字や文字数、複数候補などのパターンを使う場合は、Regexが適しています。

C#
bool result = Regex.IsMatch(
input,
@"商品番号[A-Z]{2}\d{4}"
);

目安として、固定文字列ならStringメソッド、可変的な文字パターンならRegexを利用します。正規表現で書ける処理でも、ContainsStartsWithEndsWithReplaceSplitなどで簡単に表現できるなら、通常の文字列メソッドを優先すると読みやすくなります。

まとめ

C#で正規表現を使うには、System.Text.RegularExpressions名前空間のRegexクラスを利用します。

文字列がパターンに一致するか判定する場合はRegex.IsMatch、最初の一致結果を抽出する場合はRegex.Match、すべての一致結果を取得する場合はRegex.Matchesを使います。一致部分の置換や削除にはRegex.Replaceが便利です。

C#正規表現を使う際は、次のポイントを押さえておきましょう。

  • 正規表現は@"..."の逐語的文字列リテラルで記述する

  • 完全一致には^$、または\A\zを使う

  • 抽出結果はMatch.Successを確認してから利用する

  • 複数の項目を抽出するときは名前付きグループを使う

  • 単純な文字列処理ではStringメソッドを優先する

  • 外部入力を処理するときはタイムアウトを指定する

  • ユーザー入力を固定文字列として使う場合はRegex.Escapeを利用する

  • 複雑すぎるパターンや量指定子の入れ子を避ける

  • 正規表現だけで値の実在性や意味上の妥当性まで判断しない

正規表現は文字列処理を短く柔軟に書ける一方、複雑になると可読性やパフォーマンスに問題が生じます。まずはIsMatchMatchMatchesReplaceの基本を理解し、通常の文字列メソッドや専用パーサーと適切に使い分けることが重要です。