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

C語言中高效使用正則表達式:POSIX Regex API完整指南與實戰

     編輯:關於C語言

前言:正則表達式不只是腳本語言的專利

熟悉Linux sed/awk/grep/vi 的開發者對正則表達式絕不陌生。它以聲明式模式極大簡化了複雜文本處理。雖然標準C/C++庫未內置正則支持,但POSIX Regex (regex.h) 和 PCRE (Perl-Compatible Regular Expressions) 等成熟庫填補了這一空白。本文系統講解POSIX Regex API在C語言中的完整用法:編譯、匹配、提取子串、錯誤處理、資源釋放,並給出可直接編譯運行的完整示例。

核心API四部曲

1. regcomp() — 編譯正則表達式

int regcomp(regex_t *preg, const char *regex, int cflags);
  • preg:輸出參數,指向 regex_t 結構,存儲編譯後的內部表示(狀態機)。
  • regex:正則表達式字符串(如 "regex[a-z]*")。
  • cflags:編譯標誌位(按位或):
    • REG_EXTENDED:使用擴展正則語法(默認基礎語法較弱)。
    • REG_ICASE:忽略大小寫。
    • REG_NOSUB:不保存匹配位置(僅判斷是否匹配,加速)。
    • REG_NEWLINE:. 不匹配換行符,^/$ 匹配行首尾。
  • 返回值:0 成功,非零為錯誤碼(需配合 regerror 解析)。

2. regexec() — 執行模式匹配

int regexec(const regex_t *preg, const char *string,
            size_t nmatch, regmatch_t pmatch[], int eflags);
  • string:待匹配文本。
  • nmatch / pmatch:最多捕獲 nmatch 個子表達式匹配位置,存入 pmatch 數組。
    regmatch_t 結構:{ regoff_t rm_so; // 起始偏移 regoff_t rm_eo; // 結束偏移(不含) }
  • pmatch[0] 為整個正則匹配範圍,pmatch[1..] 為捕獲組(括號內子表達式)。
  • eflags:REG_NOTBOL (行首不視為行首)、REG_NOTEOL (行尾不視為行尾)。
  • 返回值:0 匹配成功,REG_NOMATCH 未匹配,其他為錯誤。

3. regfree() — 釋放編譯資源

void regfree(regex_t *preg);

必須在不再使用 preg 時調用,避免內存洩漏。最佳實踐:每次 regcomp 後配對一次 regfree,即使標準未強制要求。

4. regerror() — 獲取人類可讀錯誤信息

size_t regerror(int errcode, const regex_t *preg,
                char *errbuf, size_t errbuf_size);
  • errcode:regcomp/regexec 返回的非零錯誤碼。
  • preg:提供上下文(如錯誤位置)。
  • 將格式化錯誤信息寫入 errbuf (最多 errbuf_size 字節),返回實際長度。

完整實戰示例:逐行匹配並高亮輸出捕獲組

#include <stdio.h>
#include <sys/types.h>
#include <regex.h>
#include <string.h>

// 截取子串輔助函數
static char* substr(const char* str, unsigned start, unsigned end) {
    static char buf[256];
    unsigned n = end - start;
    strncpy(buf, str + start, n);
    buf[n] = '\0';
    return buf;
}

int main(int argc, char** argv) {
    if (argc < 2) {
        fprintf(stderr, "Usage: %s  [file...]\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[1024];
    int lno = 0, z;

    // 1. 編譯正則
    z = regcomp(®, pattern, REG_EXTENDED);
    if (z != 0) {
        regerror(z, ®, ebuf, sizeof(ebuf));
        fprintf(stderr, "%s: pattern '%s'\n", ebuf, pattern);
        return 1;
    }

    // 2. 逐行處理標準輸入
    while (fgets(lbuf, sizeof(lbuf), stdin)) {
        ++lno;
        // 去除尾部換行符
        if ((z = strlen(lbuf)) > 0 && lbuf[z-1] == '\n')
            lbuf[z-1] = '\0';

        // 3. 執行匹配
        z = regexec(®, lbuf, nmatch, pm, 0);
        if (z == REG_NOMATCH) continue;
        if (z != 0) {
            regerror(z, ®, ebuf, sizeof(ebuf));
            fprintf(stderr, "%s: regexec('%s')\n", ebuf, lbuf);
            regfree(®);
            return 2;
        }

        // 4. 輸出結果:行號 + 原行 + 所有捕獲組
        for (int x = 0; x < nmatch && pm[x].rm_so != -1; ++x) {
            if (!x) printf("%04d: %s\n", lno, lbuf);
            printf(" $%d='%s'\n", x, substr(lbuf, pm[x].rm_so, pm[x].rm_eo));
        }
    }

    // 5. 釋放資源
    regfree(®);
    return 0;
}

編譯與測試

# gcc regexp.c -o regexp -lregex  # 鏈接 regex 庫
# ./regexp 'regex[a-z]*' < regexp.c
0003: #include <regex.h>
 $0='regex'
0027: regex_t reg;
 $0='regex'
0054: z = regexec(&reg, lbuf, nmatch, pm, 0);
 $0='regexec'
...

程序從標準輸入讀取文本(此處為自身源碼),對每行應用正則 regex[a-z]*,輸出匹配行及所有捕獲組($0為整體匹配)。

進階技巧與避坑指南

  1. 捕獲組嵌套:pmatch 數組大小需 ≥ 捕獲組數 + 1。動態分配可用 malloc(nmatch * sizeof(regmatch_t))。
  2. 多行匹配:默認 ^/$ 匹配整字符串首尾。加 REG_NEWLINE 使其匹配行首尾。
  3. 性能優化:同一正則重複使用時,一次編譯,多次匹配,最後統一 regfree。
  4. PCRE選項:需更強大語法(如 \d, \w, (?=...), (?>...)) 可改用 pcre.h,API風格相似但功能更豐富。
  5. 線程安全:regex_t 非線程安全,多線程環境下每線程獨立編譯或加鎖。

總結

POSIX Regex API 雖然接口略顯古老,但穩定、標準、無第三方依賴,是C語言項目中引入正則處理能力的首選。掌握 regcomp → regexec → regfree 三部曲,配合 regerror 診斷錯誤,即可在C項目中享受接近Perl/Python的文本處理靈活性。

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