基于PCRE2的C语言字符串替换及回调替换函数开发求助
问题解答
PCRE2是否支持自动处理内存分配大小?
PCRE2没有提供完全自动的内存分配机制,但可以通过**两次调用pcre2_substitute**实现动态内存分配:
- 第一次调用时,将输出缓冲区设为
NULL、outlen设为0,函数会返回包含终止符的所需缓冲区大小。 - 根据返回值分配内存后,第二次调用
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
相关产品推荐
相关产品推荐

