linux C语言调用pcre2正则库
专栏:ExASIC Feb. 10, 2026, 5:15 p.m. 23 阅读
一个linux C语言调用pcre2正则库的例子

使用 PCRE2,在C语言中进行正则匹配的流程稍微复杂一点,因为它提供了非常强大的控制能力。

流程通常是:

  1. 配置上下文(可选,通常用默认)。

  2. 编译 (Compile):将正则字符串转换成内部结构。

  3. 匹配 (Match):在目标字符串中搜索。

  4. 提取结果:获取匹配到的子串(Capture Groups)。

  5. 清理内存。

示例代码:提取邮箱地址

这段代码演示了如何从字符串中提取一个邮箱地址,并打印出“整个匹配”以及“用户名部分(@之前的内容)”。

前提: 你需要安装 PCRE2 库。

  • Ubuntu/Debian: sudo apt install libpcre2-dev

  • CentOS/RHEL: sudo yum install pcre2-devel

  • macOS: brew install pcre2

#define PCRE2_CODE_UNIT_WIDTH 8 // 必须定义:指明我们处理的是 8-bit (char) 字符串
#include <stdio.h>
#include <string.h>
#include <pcre2.h>

int main(int argc, char **argv) {
    pcre2_code *re;
    PCRE2_SPTR pattern;     /* PCRE2_SPTR 是 unsigned char * */
    PCRE2_SPTR subject;
    int errornumber;
    PCRE2_SIZE erroroffset;
    pcre2_match_data *match_data;
    int rc;
    PCRE2_SIZE *ovector;

    // 1. 定义正则表达式和目标字符串
    // 正则含义:匹配邮箱,并把 @ 前面的部分放入第一个捕获组 ()
    pattern = (PCRE2_SPTR)"([a-zA-Z0-9_.+-]+)@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+";
    subject = (PCRE2_SPTR)"My email is hello.world@example.com, please contact me.";
    size_t subject_length = strlen((char *)subject);

    printf("Pattern: %s\n", (char *)pattern);
    printf("Subject: %s\n", (char *)subject);

    // 2. 编译正则表达式
    re = pcre2_compile(
        pattern,               /* 正则表达式字符串 */
        PCRE2_ZERO_TERMINATED, /* 长度,如果是以 \0 结尾则用这个宏 */
        0,                     /* 默认选项 */
        &errornumber,          /* 错误码输出 */
        &erroroffset,          /* 错误偏移量输出 */
        NULL);                 /* 编译上下文,NULL 使用默认 */

    if (re == NULL) {
        PCRE2_UCHAR buffer[256];
        pcre2_get_error_message(errornumber, buffer, sizeof(buffer));
        printf("PCRE2 compilation failed at offset %d: %s\n", (int)erroroffset, buffer);
        return 1;
    }

    // 3. 准备匹配数据块 (用于存放匹配结果)
    match_data = pcre2_match_data_create_from_pattern(re, NULL);

    // 4. 执行匹配
    rc = pcre2_match(
        re,                   /* 编译好的正则对象 */
        subject,              /* 目标字符串 */
        subject_length,       /* 字符串长度 */
        0,                    /* 开始匹配的偏移量 */
        0,                    /* 匹配选项 */
        match_data,           /* 结果存放位置 */
        NULL);                /* 匹配上下文 */

    // 5. 处理结果
    if (rc < 0) {
        switch(rc) {
            case PCRE2_ERROR_NOMATCH: printf("No match found.\n"); break;
            default: printf("Matching error %d\n", rc); break;
        }
    } else {
        // rc 返回的是捕获组的数量(包括整个匹配项 0)
        // ovector 存储了匹配位置的偏移量对 (start, end)
        ovector = pcre2_get_ovector_pointer(match_data);
        printf("\nMatch success! Found %d capture groups.\n", rc);

        // 遍历所有捕获组
        // i=0 是整个匹配到的字符串
        // i=1 是第一个括号 () 内的内容,以此类推
        for (int i = 0; i < rc; i++) {
            PCRE2_SPTR substring_start = subject + ovector[2*i];
            size_t substring_len = ovector[2*i+1] - ovector[2*i];
            
            // 打印结果:%.*s 允许指定打印长度,非常适合这种情况
            printf("%d: %.*s\n", i, (int)substring_len, (char *)substring_start);
        }
    }

    // 6. 释放内存
    pcre2_match_data_free(match_data);
    pcre2_code_free(re);

    return 0;
}

编译与运行

编译时需要链接 pcre2-8 库(因为我们处理的是 8位 char)。

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

> Pattern: ([a-zA-Z0-9_.+-]+)@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+
> Subject: My email is hello.world@example.com, please contact me.
> 
> Match success! Found 2 capture groups.
> 0: hello.world@example.com
> 1: hello.world

代码解析:关键点

  1. #define PCRE2_CODE_UNIT_WIDTH 8: 这是必须的,告诉头文件我们是处理标准的 C 字符串(char),而不是宽字符(16位或32位)。

  2. pcre2_compile: 类似于 POSIX 的 regcomp,但它返回一个 pcre2_code 指针。如果失败,它不会返回错误码整数,而是返回 NULL,错误码写入参数 errornumber

  3. pcre2_match_data: 这是 PCRE2 比较独特的地方。它专门分配一块内存来存结果,不像 POSIX 那样直接传个结构体数组。这样设计是为了线程安全和灵活性。

  4. ovector (Output Vector): 这是一个 size_t 数组。

    • ovector[0] 是整个匹配开始的索引。

    • ovector[1] 是整个匹配结束的索引。

    • ovector[2] 是第1个括号捕获组开始的索引。

    • ovector[3] 是第1个括号捕获组结束的索引。

    • 以此类推...

  5. %.*s: printf 的这个格式符很实用。它允许用两个参数来打印字符串:第一个参数是长度,第二个是字符串指针。因为 C 语言字符串必须以 \0 结尾,而正则匹配出来的是原始字符串中间的一段,没有 \0,用这个方法可以直接打印而不需要拷贝内存。

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

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

最新20篇 开设专栏