linux C语言封装pcre2正则为一个函数,像python一样调用正则
专栏:ExASIC Feb. 10, 2026, 5:28 p.m. 18 阅读
设计了一个结构体 RegexResult 来承载结果,并提供了一个配套的 free 函数。

pcre2的标准使用确实有点复杂,有没有办法包成一个函数呢?比如regex_match("regex", target_string, ...) 直接返回一个group结果?这是一个非常好的想法。封装 PCRE2 的底层细节,只暴露“输入正则和字符串,输出结果数组”的接口,会让业务代码清爽很多。

在 C 语言中,主要难点在于内存管理:函数内部提取出的字符串结果(Substring)需要申请内存,调用者用完后需要释放内存。为了解决这个问题,设计了一个结构体 RegexResult 来承载结果,并提供了一个配套的 free 函数。

// easy_pcre2.c
#define PCRE2_CODE_UNIT_WIDTH 8
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <pcre2.h>

// --- 1. 定义结果结构体 ---
typedef struct {
    int count;      // 捕获组的数量 (0是整个匹配, 1是第一个括号...)
    char **groups;  // 字符串数组,存放提取出的内容
} RegexResult;

// --- 2. 封装:释放内存函数 ---
void free_regex_result(RegexResult *result) {
    if (!result) return;
    if (result->groups) {
        for (int i = 0; i < result->count; i++) {
            if (result->groups[i]) free(result->groups[i]);
        }
        free(result->groups);
    }
    free(result);
}

// --- 3. 封装:核心匹配函数 ---
// 如果匹配成功,返回 RegexResult 指针;如果失败或出错,返回 NULL
RegexResult* regex_match(const char *pattern_str, const char *subject_str) {
    pcre2_code *re;
    int errornumber;
    PCRE2_SIZE erroroffset;
    pcre2_match_data *match_data;
    int rc;
    PCRE2_SIZE *ovector;
    
    // A. 编译
    re = pcre2_compile(
        (PCRE2_SPTR)pattern_str, PCRE2_ZERO_TERMINATED, 
        0, &errornumber, &erroroffset, NULL);

    if (re == NULL) {
        // 编译失败,简单打印错误并返回 NULL
        fprintf(stderr, "Regex compile failed.\n");
        return NULL;
    }

    // B. 匹配
    match_data = pcre2_match_data_create_from_pattern(re, NULL);
    rc = pcre2_match(
        re, (PCRE2_SPTR)subject_str, strlen(subject_str), 
        0, 0, match_data, NULL);

    if (rc < 0) {
        // 没匹配到或出错
        pcre2_match_data_free(match_data);
        pcre2_code_free(re);
        return NULL; 
    }

    // C. 提取结果到自定义结构体
    RegexResult *result = (RegexResult*)malloc(sizeof(RegexResult));
    result->count = rc;
    result->groups = (char**)malloc(sizeof(char*) * rc);
    
    ovector = pcre2_get_ovector_pointer(match_data);

    for (int i = 0; i < rc; i++) {
        PCRE2_SPTR substring_start = (PCRE2_SPTR)subject_str + ovector[2*i];
        size_t substring_len = ovector[2*i+1] - ovector[2*i];

        // 申请内存并拷贝字符串,以此变成标准的 C 字符串 (\0 结尾)
        result->groups[i] = (char*)malloc(substring_len + 1);
        memcpy(result->groups[i], substring_start, substring_len);
        result->groups[i][substring_len] = '\0';
    }

    // D. 清理 PCRE2 内部对象
    pcre2_match_data_free(match_data);
    pcre2_code_free(re);

    return result;
}

// --- 4. 使用示例 ---
int main() {
    // 假设我们要提取 日期、订单号、金额
    const char *text = "Order #89757 placed on 2023-10-05 total $99.50";
    const char *pattern = "Order #(\\d+) placed on (\\d{4}-\\d{2}-\\d{2}) total \\$([0-9.]+)";

    printf("Text: %s\n", text);

    // 调用封装好的函数
    RegexResult *res = regex_match(pattern, text);

    if (res) {
        printf("Match Success!\n");
        // res->groups[0] 是整个匹配到的字符串
        // res->groups[1] 是第1个括号 (订单号)
        // res->groups[2] 是第2个括号 (日期)
        // ...
        
        if (res->count > 1) printf("Order ID: %s\n", res->groups[1]);
        if (res->count > 2) printf("Date:     %s\n", res->groups[2]);
        if (res->count > 3) printf("Amount:   %s\n", res->groups[3]);

        // 别忘了释放结果
        free_regex_result(res);
    } else {
        printf("No match found.\n");
    }

    return 0;
}

编译运行

gcc easy_pcre2.c -o easy_pcre2 -lpcre2-8 -std=gnu99
./easy_pcre2

封装后的优点

  1. 像 Python 一样简单:主函数里不需要关心 compilematch_dataovector 这些底层概念。

  2. 结果直接可用:返回的是标准的 C 字符串 (char *),可以直接拿去 strcmpatoi 或者存入数据库,不用担心没有 \0 结尾的问题。

  3. 内存安全:提供了 free_regex_result 函数,一键清理所有申请的字符串内存。

感谢阅读,更多文章点击这里:【专栏:ExASIC】
公众号:【ExASIC】

分享数字集成电路设计中的经验和方法。分享让工作更轻松。

最新20篇 开设专栏