如何用正则对比含变量的预定义文本与目标文本,返回布尔值或匹配率
含变量模板与目标文本的正则匹配实现方案
核心思路
要对比带{var}变量的预定义模板和目标文本,核心是将模板转换为正则表达式,通过正则匹配判断结构是否吻合,再计算匹配相似度:
- 模板转正则:将
{var}替换为正则捕获组(通用场景用(.*?),特定格式可替换为\d+、[A-Z]+等),同时转义正则特殊字符。 - 正则匹配:用Kannel库的
gw_regex_comp_real编译正则,执行匹配得到布尔结果。 - 匹配百分比计算:完全匹配返回100%;匹配失败时,统计模板固定文本在目标中的匹配占比,或用编辑距离计算相似度。
模板转正则规则
针对你的示例模板:
Hi {var}, Your last call was from {var} at {var} {var} was {var}. Thank you for using Cloud PBX Services.
转换后的正则(启用扩展正则+换行模式):
^Hi (.*?), Your last call was from (.*?) at (.*?) (.*?) was (.*?)\.\nThank you for using Cloud PBX Services\.
^和$锚定确保完全匹配文本整体结构(.*?)采用非贪婪匹配,避免变量内容覆盖后续固定文本- 转义
.、\n等正则特殊字符
基于Kannel库的具体实现
编译正则的Kannel函数(中文注释版)
// Kannel库的正则编译核心函数 // 参数说明: // pattern: 待编译的正则表达式(Octstr类型) // cflags: 编译标志(如REG_EXTENDED启用扩展正则,REG_NEWLINE支持换行匹配) // file/line/func: 调用位置信息(用于错误日志定位) // 返回:编译后的regex_t指针,失败返回NULL regex_t *gw_regex_comp_real(const Octstr *pattern, int cflags, const char *file, long line, const char *func) { int rc; regex_t *preg; preg = gw_malloc(sizeof(regex_t)); // 分配正则结构体内存 // 执行正则编译 if ((rc = regcomp(preg, pattern ? octstr_get_cstr(pattern) : NULL, cflags)) != 0) { char buffer[512]; regerror(rc, preg, buffer, sizeof(buffer)); // 获取编译错误信息 // 打印错误日志,包含调用上下文 error(0, "%s:%ld: %s: 正则表达式`%s`编译失败: %s (调用位置: %s:%ld:%s.)", __FILE__, (long) __LINE__, __func__, octstr_get_cstr(pattern), buffer, (file), (long) (line), (func)); gw_free(preg); // 释放已分配内存 return NULL; } return preg; }
完整匹配逻辑代码
#include <string.h> #include "kannel/octstr.h" #include "kannel/regex.h" // 将模板转换为正则表达式 Octstr *template_to_regex(const Octstr *template) { if (!template) return NULL; Octstr *regex = octstr_duplicate(template); // 先转义正则特殊字符 const char *special_chars[] = {".", "^", "$", "*", "+", "?", "|", "(", ")", "[", "]", "{", "}", "\\", NULL}; for (int i = 0; special_chars[i]; i++) { Octstr *from = octstr_create(special_chars[i]); Octstr *to = octstr_create("\\"); octstr_append(to, from); regex = octstr_replace(regex, from, to); octstr_destroy(from); octstr_destroy(to); } // 将{var}替换为非贪婪捕获组 regex = octstr_replace(regex, octstr_create("{var}"), octstr_create("(.*?)")); // 添加行首行尾锚定 octstr_prepend(regex, octstr_create("^")); octstr_append(regex, octstr_create("$")); return regex; } // 对比模板与目标文本,返回匹配结果(1=成功,0=失败),可选返回匹配百分比 int match_template(const Octstr *template, const Octstr *target, float *match_percent) { if (!template || !target) { if (match_percent) *match_percent = 0.0f; return 0; } Octstr *regex_str = template_to_regex(template); if (!regex_str) { if (match_percent) *match_percent = 0.0f; return 0; } // 编译正则:启用扩展正则+换行模式 regex_t *preg = gw_regex_comp_real(regex_str, REG_EXTENDED | REG_NEWLINE, __FILE__, __LINE__, __func__); octstr_destroy(regex_str); if (!preg) { if (match_percent) *match_percent = 0.0f; return 0; } // 执行匹配 int rc = regexec(preg, octstr_get_cstr(target), 0, NULL, 0); regfree(preg); // 释放正则结构体 if (rc == 0) { // 完全匹配成功 if (match_percent) *match_percent = 100.0f; return 1; } else { // 计算匹配百分比:统计模板固定文本在目标中的匹配占比 if (match_percent) { Octstr *fixed_part = octstr_duplicate(template); fixed_part = octstr_replace(fixed_part, octstr_create("{var}"), octstr_create("")); int fixed_len = octstr_len(fixed_part); if (fixed_len == 0) { *match_percent = 0.0f; } else { const char *fixed_cstr = octstr_get_cstr(fixed_part); const char *target_cstr = octstr_get_cstr(target); int matched_chars = 0; const char *pos = target_cstr; // 统计固定文本在目标中的出现总长度 while ((pos = strstr(pos, fixed_cstr)) != NULL) { matched_chars += octstr_len(fixed_part); pos += octstr_len(fixed_part); } *match_percent = (float)matched_chars / fixed_len * 100.0f; // 避免百分比超过100% if (*match_percent > 100.0f) *match_percent = 100.0f; } octstr_destroy(fixed_part); } return 0; } }
示例验证
- 成功示例1/2:目标文本符合模板结构,返回
1,匹配百分比100% - 失败示例:目标文本结构不符,返回
0,匹配百分比约为47%(具体数值由代码计算)
内容的提问来源于stack exchange,提问作者Vinayak
相关产品推荐
相关产品推荐

