みなさんこんにちは!ヒロポンです!
C#業務系の入力検証で、こんなことないですか??
- 新規登録フォームでメール検証の正規表現を書こうとして「正解の書き方が思い出せない」
- 電話番号のハイフン有無で正規表現が複雑になりすぎて挫折
- 全角混入チェックのUnicode範囲指定が思い出せない
Regex.IsMatchで^$忘れて部分一致で意図しないマッチ
俺も最初の業務系WinForms案件で新規登録フォーム作った時、メール検証でRFC5322厳密版をネットからコピペして、テストで「正常なメールが弾かれる」報告上がって30分溶かしました。.NET Framework 4.7.2 / VS2019 / C# 7.3の構成で、結局簡易版に戻したっていうオチ。
ん?普通にメール検証くらい簡単じゃないの??って思いますよね。
業務系の正規表現は「厳密版より現実版」で書くのが鉄則。今回は業務系入力検証で頻出するC#正規表現の5パターンを、コード付きで整理します。コード6本構成。
忙しい人向けに最初にまとめ
- 業務系の正規表現は厳密版より現実版で書く(メール検証RFC5322厳密版は使わない)
- 5パターン: メール / 電話番号 / 郵便番号 / 全角半角判定 / 数値・日付フォーマット
- 大量呼び出しは
RegexOptions.Compiledで高速化(10万件で3秒→ 0.5秒の体感) IsMatchの^$忘れに注意(部分一致で意図しないマッチ)
パターン1:メールアドレス検証—簡易版が現実解
業務系のメール検証でRFC5322厳密版を使うのは事故の元。正規表現が数百文字になって、テストで正常なメールが弾かれるクレームが上がります。
using System.Text.RegularExpressions;
public static class EmailValidator
{
// ✅業務系の現実版(簡易・ほぼ十分)
private static readonly Regex EmailRegex = new Regex(
@"^[^@\s]+@[^@\s]+\.[^@\s]+$",
RegexOptions.Compiled);
public static bool IsValid(string email)
{
if (string.IsNullOrWhiteSpace(email))return false;
return EmailRegex.IsMatch(email);
}
}
//使い方
class Program
{
static void Main()
{
Console.WriteLine(EmailValidator.IsValid("user@example.com")); // True
Console.WriteLine(EmailValidator.IsValid("user.name@sub.co.jp")); // True
Console.WriteLine(EmailValidator.IsValid("invalid@@example")); // False
Console.WriteLine(EmailValidator.IsValid("no-at-sign.com")); // False
}
}
正規表現の意味:
^$—先頭末尾アンカー(完全一致)[^@\s]+— @と空白以外の1文字以上@— @記号[^@\s]+—ドメイン部\.—ドット(エスケープ)[^@\s]+— TLD
この簡易版で業務系の登録フォーム検証は十分。本格的な検証が必要なら、メール送信時の到達確認(ダブルオプトイン)に委ねるのが業務SE鉄則。
俺も独立後の案件で、RFC5322厳密版を入れたシステムを引き継いだ時、「user+tag@example.comが弾かれる」「user.name+filter@example.comが弾かれる」のクレーム対応に追われた経験があります。厳密版を入れると保守コストが跳ね上がるので、業務系では簡易版一択です。
パターン2:電話番号検証—国内+国際対応
電話番号の正規表現は、国内専用と国際対応で書き方が違います。
using System.Text.RegularExpressions;
public static class PhoneValidator
{
// ✅国内電話番号(先頭0 +ハイフン任意)
//例: 03-1234-5678 / 090-1234-5678 / 0312345678
private static readonly Regex DomesticPhoneRegex = new Regex(
@"^0\d{1,4}-?\d{1,4}-?\d{4}$",
RegexOptions.Compiled);
// ✅国際対応版(+記号+国番号+国内番号)
//例: +81-3-1234-5678 / 03-1234-5678 / +1 555 123 4567
private static readonly Regex InternationalPhoneRegex = new Regex(
@"^(\+?\d{1,4}[-\s]?)?\d{2,5}[-\s]?\d{2,5}[-\s]?\d{2,5}$",
RegexOptions.Compiled);
public static bool IsValidDomestic(string phone)
{
if (string.IsNullOrWhiteSpace(phone))return false;
return DomesticPhoneRegex.IsMatch(phone);
}
public static bool IsValidInternational(string phone)
{
if (string.IsNullOrWhiteSpace(phone))return false;
return InternationalPhoneRegex.IsMatch(phone);
}
}
//使い方
class Program
{
static void Main()
{
//国内
Console.WriteLine(PhoneValidator.IsValidDomestic("03-1234-5678")); // True
Console.WriteLine(PhoneValidator.IsValidDomestic("0312345678")); // True
Console.WriteLine(PhoneValidator.IsValidDomestic("090-1234-5678")); // True
Console.WriteLine(PhoneValidator.IsValidDomestic("+81-3-1234-5678")); // False
//国際
Console.WriteLine(PhoneValidator.IsValidInternational("+81-3-1234-5678")); // True
Console.WriteLine(PhoneValidator.IsValidInternational("+1 555 123 4567")); // True
}
}
ハマりポイント。ハイフンを-?で任意にするのがブレ吸収のポイント。ユーザーは0312345678 / 03-1234-5678 / 03 1234 5678を混在で入力するので、[-\s]?で両方を許容するのが現実解。
業務系で国際電話を受け付ける必要があるかは、システム要件次第。海外取引のある会社のシステムなら最初から国際対応、国内オンリーなら厳密にすればいい、という判断軸を持つといい感じです。
パターン3:郵便番号検証— 7桁ハイフン形式
国内向けの郵便番号は7桁ハイフン形式で固定。書き方はシンプルです。
using System.Text.RegularExpressions;
public static class PostalCodeValidator
{
// ✅郵便番号: 7桁+ハイフン任意
//例: 123-4567 / 1234567
private static readonly Regex PostalCodeRegex = new Regex(
@"^\d{3}-?\d{4}$",
RegexOptions.Compiled);
public static bool IsValid(string postalCode)
{
if (string.IsNullOrWhiteSpace(postalCode))return false;
return PostalCodeRegex.IsMatch(postalCode);
}
//正規化(ハイフンを統一)
public static string Normalize(string postalCode)
{
if (!IsValid(postalCode))return null;
//ハイフンを取り除いて統一フォーマットに変換
var digits = postalCode.Replace("-", "");
return $"{digits.Substring(0, 3)}-{digits.Substring(3, 4)}";
}
}
//使い方
class Program
{
static void Main()
{
Console.WriteLine(PostalCodeValidator.IsValid("123-4567")); // True
Console.WriteLine(PostalCodeValidator.IsValid("1234567")); // True
Console.WriteLine(PostalCodeValidator.IsValid("12-34567")); // False
Console.WriteLine(PostalCodeValidator.Normalize("1234567")); // "123-4567"
}
}
業務系では入力の正規化(Normalize)をセットで実装するのが鉄則。ユーザー入力のブレ(ハイフン有無)を統一フォーマットに揃えてDBに保存することで、検索/表示が一貫します。
ハマりポイント。^\d{7}$だけで書くと1234567のみマッチして123-4567が弾かれます。-?の任意ハイフンが業務系の現実版。
パターン4:全角半角判定— Unicode範囲
全角文字混入チェックは業務系で頻出。Unicode範囲指定で判定します。
using System.Text.RegularExpressions;
public static class CharTypeValidator
{
// ✅全角文字(ASCII外)が含まれているか
private static readonly Regex FullWidthRegex = new Regex(
@"[^\x00-\x7F]",
RegexOptions.Compiled);
// ✅半角カナが含まれているか
private static readonly Regex HalfWidthKanaRegex = new Regex(
@"[。-゚]",
RegexOptions.Compiled);
// ✅日本語文字(ひらがな・カタカナ・漢字)のみ
private static readonly Regex JapaneseOnlyRegex = new Regex(
@"^[ぁ-んァ-ヶ一-龯]+$",
RegexOptions.Compiled);
public static bool ContainsFullWidth(string input)
{
if (string.IsNullOrEmpty(input))return false;
return FullWidthRegex.IsMatch(input);
}
public static bool ContainsHalfWidthKana(string input)
{
if (string.IsNullOrEmpty(input))return false;
return HalfWidthKanaRegex.IsMatch(input);
}
public static bool IsJapaneseOnly(string input)
{
if (string.IsNullOrEmpty(input))return false;
return JapaneseOnlyRegex.IsMatch(input);
}
}
//使い方
class Program
{
static void Main()
{
Console.WriteLine(CharTypeValidator.ContainsFullWidth("abc")); // False
Console.WriteLine(CharTypeValidator.ContainsFullWidth("abc日本語")); // True
Console.WriteLine(CharTypeValidator.ContainsFullWidth("ABCあ")); // True (全角A)
Console.WriteLine(CharTypeValidator.ContainsHalfWidthKana("アイウ")); // True
Console.WriteLine(CharTypeValidator.IsJapaneseOnly("こんにちは")); // True
Console.WriteLine(CharTypeValidator.IsJapaneseOnly("Hello")); // False
}
}
Unicode範囲のポイント:
[^\x00-\x7F]— ASCII範囲外(=全角文字全般、最も汎用的)[。-゚]—半角カナ(DB移行時のチェックで頻出)[ぁ-んァ-ヶ一-龯]—ひらがな+カタカナ+漢字(日本語のみ判定)
ハマりポイント。[-ヿ]だけで全角判定すると漢字(一-鿿)を見逃します。業務系で「全角混入チェック」する場合はASCII外を判定する[^\x00-\x7F]が汎用的で漏れがない。これ覚えておくと、漢字/全角記号/全角英数/全角空白すべてを一括判定できます。
業務系のCSV取り込みで「半角想定のカラムに全角混入してた」事故は、ContainsFullWidthをセットで仕込むだけで防げます。
パターン5:数値・日付フォーマット検証
業務系の入力検証で頻出する数値/日付フォーマット。
using System;
using System.Text.RegularExpressions;
public static class FormatValidator
{
// ✅整数(正負・桁数指定なし)
private static readonly Regex IntegerRegex = new Regex(
@"^-?\d+$",
RegexOptions.Compiled);
// ✅小数(正負・小数点任意)
private static readonly Regex DecimalRegex = new Regex(
@"^-?\d+(\.\d+)?$",
RegexOptions.Compiled);
// ✅日付YYYY/MM/DD
private static readonly Regex DateRegex = new Regex(
@"^\d{4}/\d{2}/\d{2}$",
RegexOptions.Compiled);
public static bool IsInteger(string input)
{
return !string.IsNullOrWhiteSpace(input)&& IntegerRegex.IsMatch(input);
}
public static bool IsDecimal(string input)
{
return !string.IsNullOrWhiteSpace(input)&& DecimalRegex.IsMatch(input);
}
//日付はRegex + DateTime.TryParseの二段検証
public static bool IsValidDate(string input)
{
if (string.IsNullOrWhiteSpace(input))return false;
if (!DateRegex.IsMatch(input))return false;
//フォーマット通過後、実在日付か検証(2026/02/30を弾く)
return DateTime.TryParseExact(input, "yyyy/MM/dd",
null, System.Globalization.DateTimeStyles.None, out _);
}
}
//使い方
class Program
{
static void Main()
{
Console.WriteLine(FormatValidator.IsInteger("123")); // True
Console.WriteLine(FormatValidator.IsInteger("-456")); // True
Console.WriteLine(FormatValidator.IsInteger("12.3")); // False
Console.WriteLine(FormatValidator.IsDecimal("3.14")); // True
Console.WriteLine(FormatValidator.IsDecimal("-2.5")); // True
Console.WriteLine(FormatValidator.IsValidDate("2026/05/15")); // True
Console.WriteLine(FormatValidator.IsValidDate("2026/02/30")); // False(実在しない)
}
}
日付検証は正規表現+ DateTime.TryParseExactの二段検証が業務系鉄則。正規表現だけだと2026/02/30のような実在しない日付を許可してしまうので、TryParseExactで実在チェックする組み合わせで対応します。
パターン6: RegexOptions.Compiledでパフォーマンス改善
業務系の大量入力検証ではRegexOptions.Compiledが効きます。
using System.Diagnostics;
using System.Text.RegularExpressions;
public static class RegexPerformanceComparison
{
private const string Pattern = @"^[^@\s]+@[^@\s]+\.[^@\s]+$";
// ❌都度コンパイル(遅い)
public static int CountValidNaive(string[] emails)
{
int count = 0;
foreach (var email in emails)
{
if (Regex.IsMatch(email, Pattern))
count++;
}
return count;
}
// ✅ Compiledで1回だけコンパイル(速い)
private static readonly Regex CompiledRegex = new Regex(
Pattern, RegexOptions.Compiled);
public static int CountValidCompiled(string[] emails)
{
int count = 0;
foreach (var email in emails)
{
if (CompiledRegex.IsMatch(email))
count++;
}
return count;
}
}
//使い方
class Program
{
static void Main()
{
var emails = Enumerable.Range(0, 100000)
.Select(i => $"user{i}@example.com")
.ToArray();
var sw = Stopwatch.StartNew();
int c1 = RegexPerformanceComparison.CountValidNaive(emails);
sw.Stop();
Console.WriteLine($"Naive: {sw.ElapsedMilliseconds}ms (count={c1})");
sw.Restart();
int c2 = RegexPerformanceComparison.CountValidCompiled(emails);
sw.Stop();
Console.WriteLine($"Compiled: {sw.ElapsedMilliseconds}ms (count={c2})");
}
}
体感での速度差。10万件のIsMatchで:
| 方法 | 実行時間(体感) |
|---|---|
| 都度コンパイル(Naive) | 約3秒 |
RegexOptions.Compiled版 |
約0.5秒 |
CSV取り込み10万行を検証するバッチ処理だと、この差が体感で大きく出ます。ただし初回コンパイルのコストが上乗せされるので、1回しか使わない正規表現には不向き。繰り返し使う正規表現はCompiled、1回限りなら素のRegexという判断軸を持つといい感じです。
俺の現場でも、CSV取り込み処理が遅くて調べたらRegexが都度コンパイルされてた、というハマりがありました。RegexOptions.Compiledを入れたら処理時間が一気に短くなって、いい感じに高速化できました!!
ハマりポイント整理
業務系の正規表現で踏みやすいハマり:
| ハマりポイント | 原因 | 対処 |
|---|---|---|
| メール検証で正常メールが弾かれる | RFC5322厳密版を使った | 簡易版に切り替え |
| 電話番号で国際電話が弾かれる | 国内専用パターン | 国際対応版を別用意 |
| 全角判定で漢字を見逃す | [-ヿ]範囲指定の漏れ |
[^\x00-\x7F]でASCII外判定 |
IsMatchで部分一致してしまう |
^ $忘れ |
アンカー必須化 |
| バッチ処理が遅い | 都度コンパイル | RegexOptions.Compiled |
まとめ
C#正規表現の業務系利用5パターン:
- メールアドレス —
^[^@\s]+@[^@\s]+\.[^@\s]+$の簡易版 - 電話番号 —国内
^0\d{1,4}-?\d{1,4}-?\d{4}$/国際は別パターン - 郵便番号 —
^\d{3}-?\d{4}$+ Normalizeで統一フォーマット - 全角半角判定 —
[^\x00-\x7F]でASCII外判定が汎用的 - 数値・日付 —正規表現+ DateTime.TryParseExactの二段検証
業務系では厳密版より現実版を選ぶ判断軸が鉄則。ユーザー入力のブレを吸収する簡易版の方が、メンテ性と現場の事故防止の両面で勝ちます。大量呼び出しはRegexOptions.Compiledで高速化、^ $アンカーはIsMatchで必須化、を覚えておけば業務系の入力検証はカバーできます。
よくある質問
Q1. C#でメールアドレス検証の正規表現はどう書く?
A.業務系では^[^@\s]+@[^@\s]+\.[^@\s]+$程度の簡易版が現実解です。RFC5322厳密版(数百文字の正規表現)は正常なメールを弾くケースがあって、業務系の登録フォームでクレームの種になります。簡易版で@の前後・ドット必須の3点だけチェックして、本格的な検証はメール送信時の到達確認に委ねるのが業務SE鉄則です。
Q2. C#で電話番号検証で国内と国際を両方受け入れたい時は?
A.正規表現を^(\+?\d{1,4}[-\s]?)?\d{2,5}[-\s]?\d{2,5}[-\s]?\d{2,5}$程度にすれば国内/国際両方を受け入れられます。国内のみ厳密にしたい場合は^0\d{1,4}-?\d{1,4}-?\d{4}$で先頭0を強制。業務系で国際電話を受け付ける必要があるなら最初から国際対応にする、というのが業務SE鉄則です。
Q3. C#で全角半角判定の正規表現はどう書く?
A.全角文字のUnicode範囲で判定します。日本語全般なら[ぁ-んァ-ヶ一-龯]、ASCII外(全角記号含む)なら[^\x00-\x7F]。半角カナ判定は[。-゚]。業務系で「全角混入チェック」する場合はASCII外文字を検出する[^\x00-\x7F]が汎用的で、漢字・ひらがな・カタカナ・全角記号を一括判定できます。
Q4. C# RegexのRegexOptions.Compiledはいつ使う?
A.同じ正規表現を大量呼び出しする時に使います。RegexOptions.Compiledを指定するとRegexオブジェクトをILコンパイルして、2回目以降のIsMatch / Matchが高速化されます。10万件のIsMatchで素のRegexが3秒かかるところ、Compiledで0.5秒程度(業務系体感)。初回コンパイルのコストが上乗せされるので、1回しか使わない正規表現には不向きです。
Q5.正規表現の^ $を忘れるとどうなる?
A.部分一致でマッチするので、想定外の文字列を許可してしまいます。例: \d{3}-\d{4}で郵便番号検証すると、abc123-4567xyzも「123-4567」が部分一致してマッチします。^\d{3}-\d{4}$のように先頭末尾アンカーを付けると完全一致になって、入力検証として機能します。業務系のIsMatchは^ $必須、と覚えておくと事故防げます。
関連記事
- C#文字列結合のパフォーマンス— string + / StringBuilder / string.Concatの使い分け —文字列処理パフォーマンスの隣接トピック
- C#例外処理のベストプラクティス—業務SEがtry-catch-finallyとIDisposableで詰まる時の整理 — IsMatch例外処理の隣接トピック
- C# LINQのNullハンドリングとパフォーマンス— Any / Where / FirstOrDefaultで詰まる時の整理 —パフォーマンス比較の隣接トピック
以上!
同じ罠でハマってる業務SE仲間いたら、どんどんシェア待ってるぜ!!
執筆者
バイブス父さん — 業務 SE 7 年 (正社員 2 / フリーランス 5)。 現職は SEO 直轄部の AI アドバイザー兼 PL、 副業で中小 SIer の CTO。 SES 複数社・フリーランスエージェント複数経由の経験ベースで「業務 SE 視点」 の技術 + キャリア記事を書いています。
🐦 X: @hiro_progra0524 (日々の現場メモ更新中)
📝 About Me で経歴詳細を見る


コメント