常用 C 正則庫對比
| 庫 | 特點 | 授權 |
| GNU Regex (POSIX) | 系統內建、標準 API、功能基礎 | LGPL |
| PCRE / PCRE2 | Perl 相容、功能豐富、效能高 | BSD |
| Boost.Regex | C++ 封裝、Header-only 可用 | Boost |
| RE2 | Google 開發、線性時間保證、無回溯 | BSD |
本文以標準 <regex.h> (GNU Regex) 為例。
四大核心 API
1. regcomp - 編譯模式
int regcomp(regex_t *preg, const char *pattern, int cflags);
// 成功返回 0,失敗返回錯誤碼
// cflags: REG_EXTENDED (擴展語法), REG_ICASE (忽略大小寫), REG_NOSUB (不捕獲子串)
2. regexec - 執行匹配
int regexec(const regex_t *preg, const char *string,
size_t nmatch, regmatch_t pmatch[], int eflags);
// 成功返回 0 (匹配),REG_NOMATCH (不匹配),其他錯誤碼
// nmatch/pmatch: 捕獲群組位置 (rm_so 起始, rm_eo 結束)
3. regerror - 錯誤訊息
size_t regerror(int errcode, const regex_t *preg, char *errbuf, size_t errbuf_size);
// 將錯誤碼轉為人類可讀字串
4. regfree - 釋放資源
void regfree(regex_t *preg);
// 必須呼叫避免洩漏
完整範例:Email 格式驗證
#include
#include
#include
int main() {
regex_t reg;
int reti;
char msgbuf[100];
// 編譯:擴展正則語法
reti = regcomp(®, "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$", REG_EXTENDED);
if (reti) {
regerror(reti, ®, msgbuf, sizeof(msgbuf));
fprintf(stderr, "Regex compile failed: %s\n", msgbuf);
return 1;
}
char email[256];
while (1) {
printf("Enter email: ");
if (!fgets(email, sizeof(email), stdin)) break;
email[strcspn(email, "\n")] = 0; // 去除換行
reti = regexec(®, email, 0, NULL, 0);
if (!reti) {
puts("Valid email");
} else if (reti == REG_NOMATCH) {
puts("Invalid email");
} else {
regerror(reti, ®, msgbuf, sizeof(msgbuf));
fprintf(stderr, "Match error: %s\n", msgbuf);
}
}
regfree(®);
return 0;
}
POSIX 擴展正則語法重點
| 元字元 | 含義 |
. | 任意單一字元 (除換行) |
[...] | 字元類別 |
[^...] | 反向字元類別 |
* + ? {m,n} | 量詞:0或多、1或多、0或1、m到n次 |
() | 捕獲群組 |
| | 或 |
^ $ | 行首/行尾 |
\ | 轉義 |
捕獲群組位置提取範例
regmatch_t pmatch[3]; // 0: 整體, 1: 第一群組, 2: 第二群組
reti = regexec(®, "user@example.com", 3, pmatch, 0);
if (!reti) {
// 整體匹配
for (int i = pmatch[0].rm_so; i < pmatch[0].rm_eo; i++) putchar(email[i]);
puts("");
// 第 1 群組 (用戶名)
for (int i = pmatch[1].rm_so; i < pmatch[1].rm_eo; i++) putchar(email[i]);
puts("");
}
常見陷阱與最佳實踐
- 雙重轉義:C 字串字面值中
\ 需寫為 \\,正則引擎再見到 \。如 \\. 匹配字面點號。
- 編譯一次,多次匹配:
regcomp 開銷大,循環外編譯。
- 檢查 regcomp 回傳值:模式語法錯誤會導致編譯失敗。
- 必須 regfree:
regex_t 內部動態分配記憶體。
- 執行緒安全:
regex_t 非執行緒安全,多執行緒各自編譯或加鎖。
- 效能考量:複雜模式 (嵌套量詞) 可能導致災難性回溯,考慮 RE2 或限制輸入長度。
- 上一篇文章: C 語言解析 properties 設定檔 (Key=Value 格式) 完整實現
- 下一篇文章: C 語言 32+5+1 個關鍵字完整分類速查表 (C89/C99/C11)
Copyright ©
程式碼之家 All Rights Reserved