You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于PCRE2的C语言字符串替换及回调替换函数开发求助

问题解答

PCRE2是否支持自动处理内存分配大小?

PCRE2没有提供完全自动的内存分配机制,但可以通过**两次调用pcre2_substitute**实现动态内存分配:

  1. 第一次调用时,将输出缓冲区设为NULL、outlen设为0,函数会返回包含终止符的所需缓冲区大小。
  2. 根据返回值分配内存后,第二次调用pcre2_substitute完成替换。

不过这种方式不适用于带回调的场景——因为每个替换结果的长度由回调决定,需要手动处理内存的动态扩展。

实现带回调的gsub函数(类似Ruby的str.gsub回调)

以下是基于你现有代码改造的版本,核心逻辑是遍历所有匹配项,对每个匹配调用回调函数获取替换字符串,再逐段拼接最终结果:

#define PCRE2_CODE_UNIT_WIDTH 8
#include <pcre2.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>

// 回调函数类型:接收匹配到的字符串,返回动态分配的替换字符串(后续由gsub函数统一释放)
typedef char* (*gsub_callback)(const char* match);

char* gsub_callback_replace(const char* str, const char* reg, gsub_callback callback) {
    static const char* error_str = "内存分配失败";
    int error;
    PCRE2_SIZE erroffset;
    const PCRE2_SPTR pattern = (PCRE2_SPTR)reg;
    const PCRE2_SPTR subject = (PCRE2_SPTR)str;

    // 编译正则表达式
    pcre2_code* re = pcre2_compile(pattern, PCRE2_ZERO_TERMINATED, 0, &error, &erroffset, NULL);
    if (re == NULL) {
        fputs(error_str, stderr);
        return NULL;
    }
    pcre2_jit_compile(re, PCRE2_JIT_COMPLETE);

    // 初始化匹配数据结构
    pcre2_match_data* match_data = pcre2_match_data_create_from_pattern(re, NULL);
    if (match_data == NULL) {
        pcre2_code_free(re);
        fputs(error_str, stderr);
        return NULL;
    }

    PCRE2_SIZE subject_len = strlen(str);
    PCRE2_SIZE start_offset = 0;
    char* result = malloc(1); // 初始化为空字符串
    if (result == NULL) goto cleanup_error;
    size_t result_len = 0;

    // 遍历所有匹配项
    while (pcre2_match(re, subject, subject_len, start_offset, 0, match_data, NULL) >= 0) {
        PCRE2_SIZE* ovector = pcre2_get_ovector_pointer(match_data);
        PCRE2_SIZE match_start = ovector[0];
        PCRE2_SIZE match_end = ovector[1];

        // 1. 拼接匹配项之前的未匹配内容
        size_t prefix_len = match_start - start_offset;
        char* temp = realloc(result, result_len + prefix_len + 1);
        if (temp == NULL) goto cleanup_error;
        result = temp;
        strncpy(result + result_len, (const char*)subject + start_offset, prefix_len);
        result_len += prefix_len;
        result[result_len] = '\0';

        // 2. 提取匹配到的字符串
        size_t match_len = match_end - match_start;
        char* match_str = malloc(match_len + 1);
        if (match_str == NULL) goto cleanup_error;
        strncpy(match_str, (const char*)subject + match_start, match_len);
        match_str[match_len] = '\0';

        // 3. 调用回调获取替换字符串
        char* replace_str = callback(match_str);
        free(match_str);
        if (replace_str == NULL) goto cleanup_error;

        // 4. 拼接替换字符串到结果
        size_t replace_len = strlen(replace_str);
        temp = realloc(result, result_len + replace_len + 1);
        if (temp == NULL) {
            free(replace_str);
            goto cleanup_error;
        }
        result = temp;
        strcpy(result + result_len, replace_str);
        result_len += replace_len;
        free(replace_str); // 释放回调返回的内存

        // 更新偏移量,继续查找下一个匹配
        start_offset = match_end;
    }

    // 拼接最后一段未匹配的内容
    size_t suffix_len = subject_len - start_offset;
    char* temp = realloc(result, result_len + suffix_len + 1);
    if (temp == NULL) goto cleanup_error;
    result = temp;
    strncpy(result + result_len, (const char*)subject + start_offset, suffix_len);
    result_len += suffix_len;
    result[result_len] = '\0';

    // 清理资源并返回结果
    pcre2_match_data_free(match_data);
    pcre2_code_free(re);
    return result;

cleanup_error:
    free(result);
    pcre2_match_data_free(match_data);
    pcre2_code_free(re);
    fputs(error_str, stderr);
    return NULL;
}

代码说明

  • 定义gsub_callback类型,约束回调函数的输入输出:输入是匹配到的字符串,输出是动态分配的替换字符串。
  • 通过pcre2_match循环遍历所有匹配项,逐段拼接未匹配内容、回调返回的替换内容。
  • 每次拼接前用realloc动态扩展结果缓冲区,避免内存浪费。
  • 所有动态分配的内存(包括回调返回的字符串)都由函数统一释放,避免内存泄漏。

使用示例

// 示例回调:将匹配到的字符串转为大写
char* to_upper_callback(const char* match) {
    char* upper = malloc(strlen(match) + 1);
    if (upper == NULL) return NULL;
    for (size_t i = 0; match[i]; i++) {
        upper[i] = toupper(match[i]);
    }
    upper[strlen(match)] = '\0';
    return upper;
}

int main() {
    const char* test_str = "hello world, hello pcre2";
    char* result = gsub_callback_replace(test_str, "hello", to_upper_callback);
    if (result != NULL) {
        printf("%s\n", result); // 输出:HELLO world, HELLO pcre2
        free(result);
    }
    return 0;
}

内容的提问来源于stack exchange,提问作者Gregory Cohen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 18:45:33