正則表達式是文本處理的瑞士軍刀。本文匯總**高頻驗證模式**、**核心語法詳解**、**C# Regex 進階用法 (命名分組、替換委托、預編譯、Span 支持)**、以及**性能優化陷阱**,作為開發隨查手冊。
一、高頻驗證模式庫 (可直接複製)
| 場景 | 正則模式 | 說明 |
| 純數字 | ^\d+$ / ^[0-9]+$ | 至少一位 |
| 固定長度數字 (n 位) | ^\d{n}$ | 如 ^\d{6}$ 驗證 6 位郵編 |
| 範圍長度數字 (m-n 位) | ^\d{m,n}$ | |
| 非零開頭整數 | ^[1-9]\d*$ | |
| 正浮點數 (2 位小數) | ^\d+(\.\d{1,2})?$ | 金額驗證常用 |
| Email | ^[\w-\.]+@([\w-]+\.)+[\w-]{2,}$ | 簡易版,嚴格 RFC 5322 極其複雜 |
| 手機號 (中國大陸) | ^1[3-9]\d{9}$ | 包含虛擬運營商段 |
| 身份證號 (18 位) | ^\d{17}[\dXx]$ | 僅格式,校驗碼需算法驗證 |
| URL (HTTP/HTTPS) | ^https?://([\w-]+\.)+[\w-]+(/[\w-./?%&=]*)?$ | |
| IPv4 地址 | ^((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)$ | |
| 中文字符 | ^[\u4e00-\u9fa5]+$ | 基本區塊 |
| 中文+字母+數字+下劃線 | ^[\u4e00-\u9fa5a-zA-Z0-9_]+$ | 用戶名常用 |
| 日期 (yyyy-MM-dd) | ^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$ | 格式校驗,邏輯有效性需代碼判斷 |
| 密碼 (8-16位,含大小寫數字特殊) | ^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,16}$ | 零寬斷言 |
| 去除 HTML 標簽 | <[^>]*> | 替換為空串 |
| 提取 URL 域名 | https?://([^/]+) | 分組 1 為域名 |
二、核心語法速查
| 符號 | 含義 | 示例匹配 |
. | 任意單字符 (除換行符,加 Singleline 含換行) | a.c → "abc", "a-c" |
\d | 數字 [0-9] | |
\w | 單詞字符 [a-zA-Z0-9_] | |
\s | 空白字符 (空格、制表、換行) | |
\D \W \S | 非數字、非單詞、非空白 | |
^ | 字符串開始 (多行模式下行首) | |
$ | 字符串結束 (多行模式下行尾) | |
* | 零次或多次 {0,} | |
+ | 一次或多次 {1,} | |
? | 零次或一次 {0,1} | |
{n} | 恰好 n 次 | |
{n,} | 至少 n 次 | |
{n,m} | n 到 m 次 | |
? (量詞後) | **非貪婪/懶惰匹配** (匹配最少) | .*? 最少字符 |
() | **捕獲組** (編號 1,2...) | (\d{4})-(\d{2}) |
(?:) | **非捕獲組** (不佔用編號,分組用) | (?:\d{3}-){2} |
(?<name>) | **命名捕獲組** (C# 推薦用) | (?<year>\d{4}) |
(?=) | **正向預查** (零寬斷言,不消耗字符) | \d(?=\d) 後面有數字的數字 |
(?!) | **負向預查** | \d(?!\\d) 後面非數字的數字 |
(?<=) | **正向後顧** (.NET 支持變長) | (?<=\d)\d 前面有數字的數字 |
(? | **負向後顧** | |
| | 或 | cat|dog |
[] | 字符集 | [aeiou] |
[^] | 排除字符集 | [^0-9] |
\b | 單詞邊界 | |
\k<name> | 反向引用命名組 | (?<q>['""]).*?\k<q> |
三、C# Regex 進階實戰模式
1. 命名分組 + 強類型提取 (推薦)
string pattern = @"^(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})$";
string input = "2023-10-01";
var match = Regex.Match(input, pattern);
if (match.Success)
{
int year = int.Parse(match.Groups["year"].Value);
int month = int.Parse(match.Groups["month"].Value);
int day = int.Parse(match.Groups["day"].Value);
// match.Groups["year"] 等同 match.Groups[1],但可讀性強、重構安全
}
2. 替換委托:動態計算替換值
// 將所有數字乘以 2
string input = "價格: 10, 20, 30";
string result = Regex.Replace(input, @"\d+", m => (int.Parse(m.Value) * 2).ToString());
// 結果: "價格: 20, 40, 60"
// 複雜邏輯:根據上下文決定替換
result = Regex.Replace(input, @"(?<prefix>\$)(?<amount>\d+)", m =>
$"{m.Groups["prefix"].Value}{decimal.Parse(m.Groups["amount"].Value) * 1.1m:F2}");
3. 分割保留分隔符
// 保留標點符號分割句子
string text = "你好!世界。真好?";
var parts = Regex.Split(text, @"(?<=[。!?])");
// 結果: ["你好!", "世界。", "真好?", ""]
4. 多行模式處理日志/代碼
string log = @"2023-10-01 10:00:01 INFO 啟動
2023-10-01 10:00:02 ERROR 連接失敗
2023-10-01 10:00:03 WARN 重試";
// ^ 和 $ 匹配行首行尾
var errors = Regex.Matches(log, @"^.*ERROR.*$", RegexOptions.Multiline);
// 單行模式:. 匹配換行
var block = Regex.Match(log, @"ERROR.*?WARN", RegexOptions.Singleline);
5. .NET 7+ Span/ReadOnlySpan 支持 (零分配)
// Regex.IsMatch / Match / Count / EnumerateMatches 支持 ReadOnlySpan
ReadOnlySpan span = "abc123def".AsSpan();
bool hasDigit = Regex.IsMatch(span, @"\d"); // true, 無字符串分配
// 枚舉匹配 (高性能,無 Match 對象分配)
foreach (var match in Regex.EnumerateMatches(span, @"\d+"))
{
// match 是 ValueMatch 結構體,包含 Index, Length
var numSpan = span.Slice(match.Index, match.Length);
int num = int.Parse(numSpan); // .NET 7+ 支持 Span 解析
}
四、RegexOptions 詳解
| 選項 | 效果 | 常用場景 |
IgnoreCase | 忽略大小寫 | 關鍵字搜索、HTML 標簽匹配 |
Multiline | ^/$ 匹配行首尾 | 多行文本逐行處理 |
Singleline | . 匹配換行符 | 跨行匹配 HTML/代碼塊 |
ExplicitCapture | 僅捕獲命名組 (?<name>),普通 () 變非捕獲 | 大型正則優化、避免無意捕獲 |
Compiled | 編譯為 IL,**啟動慢、匹配快** | **高頻複用的靜態正則** (靜態 readonly 字段) |
IgnorePatternWhitespace | 忽略模式中的空白,允許註釋 # | 編寫可讀性強的複雜正則 |
RightToLeft | 從右向左匹配 | 特定解析場景 |
ECMAScript | 兼容 JS 正則行為 | 前後端共用正則 |
CultureInvariant | 忽略文化差異 (如土耳其語 I/ı) | 全球化應用 |
五、性能優化鐵律
- 靜態預編譯:高頻正則定義為 `static readonly Regex _regex = new Regex(pattern, RegexOptions.Compiled);` 避免重複編譯。
- 避免災難性回溯:嵌套量詞
(a+)+、互斥選項重疊 (a|a*)+。使用**原子組** (?>...) 或**佔有優先量詞** ++ *+ ?+ (需 .NET 支持) 防回溯。
- 錨點優先:模式開頭加
^ 或 \b,失敗快。
- 具體化字符集:用
[a-f0-9] 代替 . 或 \w。
- 複用 Match/Enumerator:大文本用
EnumerateMatches (Span) 或 Match.NextMatch(),避免 Matches() 一次性分配集合。
- 緩存實例:`Regex` 實例**線程安全**,可全局靜態緩存。
六、常用工具方法封裝
public static class RegexHelper
{
// 預編譯常用正則
private static readonly Regex _emailRegex = new Regex(
@"^[\w-\.]+@([\w-]+\.)+[\w-]{2,}$",
RegexOptions.Compiled | RegexOptions.IgnoreCase);
private static readonly Regex _mobileRegex = new Regex(
@"^1[3-9]\d{9}$",
RegexOptions.Compiled);
public static bool IsEmail(string s) => !string.IsNullOrEmpty(s) && _emailRegex.IsMatch(s);
public static bool IsMobile(string s) => !string.IsNullOrEmpty(s) && _mobileRegex.IsMatch(s);
// 通用提取首個匹配分組值
public static string Extract(string input, string pattern, string groupName = "value")
{
var m = Regex.Match(input, pattern);
return m.Success ? m.Groups[groupName].Value : null;
}
// 高亮關鍵字 (HTML)
public static string Highlight(string html, string keyword, string cssClass = "highlight")
{
if (string.IsNullOrEmpty(keyword)) return html;
string escaped = Regex.Escape(keyword);
return Regex.Replace(html, $"({escaped})", $"$1", RegexOptions.IgnoreCase);
}
}
總結
正則表達式威力巨大,但**可讀性差、易出災難性回溯、調試困難**。原則:**能用字符串方法 (`Contains`、`StartsWith`、`Split`、`IndexOf`、`Span` 切片) 解決的,絕不寫正則**;必須用時:**命名分組、靜態預編譯、詳盡註釋、單元測試覆蓋邊界**。推薦工具:regex101.com (實時調試、解釋、代碼生成)、RegexStorm (.NET 專用測試)。