首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C 語言正則表達式實戰:POSIX regex.h 與 PCRE 庫完整指南

     編輯:關於C語言

前言

C 標準庫不內建正則表達式,主流方案有二:

  1. POSIX ``:系統內建 (Linux/macOS/BSD/Windows POSIX 子系統),零依賴,功能基礎 (BRE/ERE)。
  2. PCRE (Perl Compatible Regular Expressions):功能豐富 (回溯引用、零寬斷言、遞迴)、效能優異、需額外鏈結 ``。

本文以 POSIX 為基礎,補充 PCRE 差異,給出完整編譯、匹配、擷取、替換範例。

一、POSIX regex.h 核心 API

函式用途
`regcomp(regex_t *preg, const char *pattern, int cflags)`編譯模式 → `regex_t` 結構
`regexec(const regex_t *preg, const char *string, size_t nmatch, regmatch_t pmatch[], int eflags)`匹配字串,填充 `pmatch` 擷取群組
`regerror(int errcode, const regex_t *preg, char *errbuf, size_t errbuf_size)`錯誤碼轉人類可讀訊息
`regfree(regex_t *preg)`釋放編譯後資源

關鍵旗標

  • `REG_EXTENDED`:使用 ERE (擴充正則),預設為 BRE (基本正則),元字元需轉義不同。
  • `REG_ICASE`:忽略大小寫。
  • `REG_NEWLINE`:`^` `$` 匹配行首尾而非整字串,`.` 不匹配換行。
  • `REG_NOSUB`:僅判斷匹配與否,不記錄擷取位置 (加速)。

二、完整範例:解析 HTTP 請求行

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <regex.h>

#define MAX_MATCH 10

int main(void) {
    const char *request = "GET /api/user?id=123 HTTP/1.1";
    const char *pattern = "^([A-Z]+) ([^ ]+) HTTP/([0-9.]+)$";

    regex_t regex;
    regmatch_t matches[MAX_MATCH];
    char errbuf[256];

    /* 1. 編譯 */
    int rc = regcomp(&regex, pattern, REG_EXTENDED);
    if (rc != 0) {
        regerror(rc, &regex, errbuf, sizeof(errbuf));
        fprintf(stderr, "regcomp 失敗: %s\n", errbuf);
        return 1;
    }

    /* 2. 匹配 */
    rc = regexec(&regex, request, MAX_MATCH, matches, 0);
    if (rc == 0) {
        printf("匹配成功!\n");
        for (size_t i = 0; i < MAX_MATCH && matches[i].rm_so != -1; ++i) {
            int start = matches[i].rm_so;
            int len = matches[i].rm_eo - matches[i].rm_so;
            printf("  群組 %zu: [%.*s]\n", i, len, request + start);
        }
    } else if (rc == REG_NOMATCH) {
        printf("不匹配\n");
    } else {
        regerror(rc, &regex, errbuf, sizeof(errbuf));
        fprintf(stderr, "regexec 錯誤: %s\n", errbuf);
    }

    /* 3. 釋放 */
    regfree(&regex);
    return 0;
}

輸出

匹配成功!
  群組 0: [GET /api/user?id=123 HTTP/1.1]
  群組 1: [GET]
  群組 2: [/api/user?id=123]
  群組 3: [1.1]

三、進階技巧

1. 多次匹配 (掃描全字串)

const char *text = "ab12cd34ef56";
const char *pat = "[0-9]+";
regex_t regex;
regcomp(&regex, pat, REG_EXTENDED);

regmatch_t match;
const char *p = text;
while (regexec(&regex, p, 1, &match, 0) == 0) {
    printf("找到數字: [%.*s] 位置 %ld\n",
           match.rm_eo - match.rm_so, p + match.rm_so, p - text + match.rm_so);
    p += match.rm_eo;  // 移至匹配後繼續
}
regfree(&regex);

2. 替換 (手動構建新字串)

POSIX 無內建替換,需自行:匹配 → 複製前綴 → 附加替換字串 → 移動指標 → 重複 → 複製尾綴。

3. 避免重複編譯

`regcomp` 較耗時,相同模式多次匹配時,編譯一次、全域/靜態快取 `regex_t`、多執行緒需鎖保護。

四、PCRE 庫快速上手

編譯鏈結

gcc -o pcre_demo pcre_demo.c -lpcre

核心差異

特性POSIXPCRE
語法BRE/EREPerl 相容 (支援 `\d`、`\w`、`(?=...)`、`(?!...)`、`\1` 回溯引用、`(?(cond)yes|no)` 條件分支)
API 風格`regcomp`/`regexec``pcre_compile`/`pcre_exec`/`pcre_free`
效能NFA 回溯,較慢優化 NFA + JIT 可選,快 5-10 倍
Unicode需 `REG_UTF8` (GNU 延伸)原生支援 `PCRE_UTF8`

PCRE 範例:命名擷取群組

#include <pcre.h>
#include <stdio.h>

int main(void) {
    const char *pat = "(?<year>\\d{4})-(?<mon>\\d{2})-(?<day>\\d{2})";
    const char *subj = "2025-05-14";
    const char *err; int erroff;
    pcre *re = pcre_compile(pat, PCRE_UTF8, &err, &erroff, NULL);
    if (!re) { fprintf(stderr, "編譯失敗: %s at %d\n", err, erroff); return 1; }

    int ovector[30];
    int rc = pcre_exec(re, NULL, subj, strlen(subj), 0, 0, ovector, 30);
    if (rc > 0) {
        for (int i = 0; i < rc; ++i) {
            const char *name; int n;
            pcre_get_named_substring(re, subj, ovector, rc, NULL, &name, &n); // 簡化
            printf("群組 %d: [%.*s]\n", i, ovector[2*i+1]-ovector[2*i], subj+ovector[2*i]);
        }
    }
    pcre_free(re);
    return 0;
}

五、效能與安全建議

  1. 預編譯快取:熱路徑模式啟動時編譯一次,結構體放入物件/模組上下文。
  2. 避免災難性回溯:模式如 `^(a+)+$` 匹配 `aaaaaaaaaaaa!` 會指數級回溯。PCRE 可用 `PCRE_NO_AUTO_POSSESS` 或重寫模式 (具體量詞 `a++`、原子群組 `(?>...)`)。
  3. 輸入長度限制:防止 ReDoS,匹配前檢查 `strlen(input) < MAX_LEN`。
  4. 執行緒安全:`regex_t`/`pcre*` 編譯後唯讀,多執行緒並發 `regexec`/`pcre_exec` 安全;編譯/釋放需互斥。
  5. 錯誤處理:永遠檢查 `regcomp`/`pcre_compile` 回傳值,生產碼不可忽略。

C 做正則表達式,「POSIX 輕量零依賴、PCRE 功能強效能高」。根據專案依賴預算與模式複雜度選型,並務必建立「編譯一次、多次匹配、錯誤必檢」的工程化規範。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved