正則表達式是處理複雜字符串匹配、提取、替換的利器,廣泛應用於 Linux 工具(sed、awk、grep、vi)及腳本語言(Perl、Python、Bash)。C 語言雖無內建支持,但通過 POSIX 標準的 regex.h API(如 Philip Hazel 的 PCRE 庫、系統自帶 libregex),可在 C 程序中獲得強大的正則處理能力。本文系統講解編譯、匹配、釋放、錯誤處理全流程,並給出完整實戰代碼。
// 1. 編譯正則表達式 → regex_t 結構
int regcomp(regex_t *preg, const char *regex, int cflags);
// 2. 執行匹配 → regmatch_t 數組
int regexec(const regex_t *preg, const char *string,
size_t nmatch, regmatch_t pmatch[], int eflags);
// 3. 釋放資源
void regfree(regex_t *preg);
// 4. 錯誤信息獲取
size_t regerror(int errcode, const regex_t *preg, char *errbuf, size_t errbuf_size);
typedef struct {
regoff_t rm_so; // 匹配起始偏移
regoff_t rm_eo; // 匹配結束偏移
} regmatch_t;
// 整個正則匹配:pmatch[0]
// 第 1 個捕獲組:pmatch[1]
// 第 n 個捕獲組:pmatch[n]
regex_t reg;
int cflags = REG_EXTENDED; // 使用擴展正則語法(ERE)
// 可選標誌:REG_ICASE(忽略大小寫)、REG_NOSUB(不捕獲子表達式)、REG_NEWLINE(識別換行)
int ret = regcomp(®, pattern, cflags);
if (ret != 0) {
char errbuf[256];
regerror(ret, ®, errbuf, sizeof(errbuf));
fprintf(stderr, "Regex compile failed: %s\n", errbuf);
return -1;
}
#define MAX_MATCH 10
regmatch_t pm[MAX_MATCH];
ret = regexec(®, input_string, MAX_MATCH, pm, 0);
if (ret == REG_NOMATCH) {
// 無匹配
} else if (ret != 0) {
// 匹配過程出錯
char errbuf[256];
regerror(ret, ®, errbuf, sizeof(errbuf));
fprintf(stderr, "Regex match error: %s\n", errbuf);
} else {
// 成功匹配
for (int i = 0; i < MAX_MATCH && pm[i].rm_so != -1; i++) {
// pm[i].rm_so 到 pm[i].rm_eo 為第 i 個匹配子串
int start = pm[i].rm_so;
int end = pm[i].rm_eo;
int len = end - start;
printf("Match %d: [%.*s]\n", i, len, input_string + start);
}
}
regfree(®); // 必須調用,釋放 regcomp 分配的內部緩衝區
// 建議:每次 regcomp 後配對一次 regfree,即使立即重新編譯
regerror 將錯誤碼轉為可讀字符串,errbuf_size 限制輸出長度,返回實際信息長度。
#include
#include
#include
#include
#include
// 安全子串提取
static char* substr(const char* str, unsigned start, unsigned end) {
unsigned n = end - start;
static char stbuf[256];
if (n >= sizeof(stbuf)) n = sizeof(stbuf) - 1;
strncpy(stbuf, str + start, n);
stbuf[n] = '\0';
return stbuf;
}
int main(int argc, char** argv) {
if (argc < 2) {
fprintf(stderr, "Usage: %s \n", argv[0]);
return 1;
}
char *pattern = argv[1];
regex_t reg;
regmatch_t pm[10];
const size_t nmatch = 10;
char ebuf[128], lbuf[256];
int lno = 0, ret;
// 編譯正則
ret = regcomp(®, pattern, REG_EXTENDED);
if (ret != 0) {
regerror(ret, ®, ebuf, sizeof(ebuf));
fprintf(stderr, "Compile error: %s (pattern: %s)\n", ebuf, pattern);
return 1;
}
// 逐行處理標準輸入
while (fgets(lbuf, sizeof(lbuf), stdin)) {
lno++;
// 移除換行符
int len = strlen(lbuf);
if (len > 0 && lbuf[len-1] == '\n') lbuf[--len] = '\0';
// 執行匹配
ret = regexec(®, lbuf, nmatch, pm, 0);
if (ret == REG_NOMATCH) continue; // 無匹配跳過
if (ret != 0) {
regerror(ret, ®, ebuf, sizeof(ebuf));
fprintf(stderr, "Match error: %s (line: %s)\n", ebuf, lbuf);
regfree(®);
return 2;
}
// 輸出匹配結果
for (int i = 0; i < nmatch && pm[i].rm_so != -1; i++) {
if (i == 0) printf("%04d: %s\n", lno, lbuf); // 整行
printf(" $%d=%s\n", i, substr(lbuf, pm[i].rm_so, pm[i].rm_eo));
}
}
regfree(®);
return 0;
}
# 編譯
gcc regexp.c -o regexp -lregex
# 測試:匹配以 regex 開頭的單詞
./regexp 'regex[a-z]*' < regexp.c
# 輸出示例:
# 0003: #include
# $0=regex
# 0027: regex_t reg;
# $0=regex
# 0054: z = regexec(?, lbuf, nmatch, pm, 0);
# $0=regexec
| 標誌 | 作用 |
|---|---|
| REG_EXTENDED | 使用擴展正則語法(ERE),功能更強 |
| REG_ICASE | 忽略大小寫匹配 |
| REG_NOSUB | 不計算子表達式匹配位置,加速匹配 |
| REG_NEWLINE | ^/$ 匹配行首尾,. 不匹配換行 |
| 標誌 | 作用 |
|---|---|
| REG_NOTBOL | 字符串非行首,^ 不匹配 |
| REG_NOTEOL | 字符串非行尾,$ 不匹配 |
// 模式:(\\d{4})-(\\d{2})-(\\d{2}) 提取年月日
const char *pattern = "(\\d{4})-(\\d{2})-(\\d{2})";
// 匹配後:
// pm[0] = 整個匹配 "2024-01-15"
// pm[1] = 年 "2024"
// pm[2] = 月 "01"
// pm[3] = 日 "15"
const char *str = "ab12cd34ef56";
const char *pattern = "([a-z]+)(\\d+)";
regex_t reg;
regcomp(®, pattern, REG_EXTENDED);
regmatch_t pm[3];
int offset = 0;
while (1) {
int ret = regexec(®, str + offset, 3, pm, 0);
if (ret == REG_NOMATCH) break;
if (ret != 0) { /* error */ break; }
printf("Full: %.*s, Letters: %.*s, Digits: %.*s\n",
pm[0].rm_eo - pm[0].rm_so, str + offset + pm[0].rm_so,
pm[1].rm_eo - pm[1].rm_so, str + offset + pm[1].rm_so,
pm[2].rm_eo - pm[2].rm_so, str + offset + pm[2].rm_so);
offset += pm[0].rm_eo; // 移動到下一個匹配位置
if (pm[0].rm_so == pm[0].rm_eo) offset++; // 防止零長度匹配死循環
}
regfree(®);
POSIX Regex 功能有限(不支持非貪婪、前瞻/後顧、遞歸等高級特性)。生產環境推薦 PCRE (Perl Compatible Regular Expressions):
pcre_compile/pcre_exec/pcre_freepcrecpp、boost::regex、std::regex (C++11)REG_NOSUB 或非捕獲組 (?:...)^、$、\b 錨定,減少回溯(a*)*、交替分支過多 (a|b|c|d|e)*regex_t 對象POSIX Regex API (regcomp/regexec/regfree/regerror) 為 C 語言提供了標準化的正則表達式處理能力。掌握編譯、匹配、捕獲組提取、錯誤處理、資源釋放全流程,配合 REG_EXTENDED 等標誌靈活控制行為,即可在 C 程序中實現類 Perl 的靈活字符串處理。對於高性能、高級特性需求,升級至 PCRE 庫是最佳選擇。