在C语言中如何获取字符串中首个正则匹配结果的指针?
问题
我正在自行实现grep工具,在结合正则表达式使用-o选项时遇到了问题。以下代码针对普通字符串匹配可以正常工作:
void do_o(char *pattern, char *line, flags options) { char *copy; char *temp; long unsigned int i; copy = line; if (options.v) { do_o_v(pattern, line, options); } else { if (options.i) { while (copy && strcasestr(copy, pattern)) { i = 0; temp = strcasestr(copy, pattern); while (i < strlen(pattern)) { printf("%c", temp[i]); i++; } printf("\n"); copy = strcasestr(copy, pattern) + strlen(pattern); } } else { while (copy && strstr(copy, pattern)) { i = 0; temp = strstr(copy, pattern); while (i < strlen(pattern)) { printf("%c", temp[i]); i++; } printf("\n"); copy = strstr(copy, pattern) + strlen(pattern); } } } }
但我需要针对正则表达式实现相同逻辑,因此需要一个类似strstr()或strcasestr()且支持正则的函数。搜索相关示例时,大多看到使用regexec()和regcomp()函数,但它们返回整数而非我需要的指针。请问如何获取字符串中首个正则匹配结果的指针?另外我需要纯C语言实现的方案。
解决方案
C标准库的regexec()虽然不直接返回匹配指针,但可以通过regmatch_t结构体获取匹配位置的偏移量,进而计算出匹配结果的指针。具体实现逻辑如下:
1. 编译正则表达式
用regcomp()编译正则模式,根据需求设置flags:
REG_EXTENDED:使用扩展正则表达式语法(和grep默认行为一致)REG_ICASE:忽略大小写(对应grep的-i选项)REG_NEWLINE:让^和$匹配换行符的开头/结尾,同时让.不匹配换行符
2. 获取匹配指针
调用regexec()时传入regmatch_t数组,该结构体的rm_so成员是匹配内容在字符串中的起始偏移量(从0开始),rm_eo是结束偏移量。通过当前字符串指针 + match[0].rm_so即可得到匹配结果的起始指针,匹配长度为match[0].rm_eo - match[0].rm_so。
3. 循环匹配(模拟原逻辑)
每次匹配完成后,从当前匹配的结束位置之后继续查找,直到无匹配结果为止。
适配正则的示例代码
#include <regex.h> void do_o_regex(char *pattern, char *line, flags options) { regex_t reg; int ret; regmatch_t match[1]; // 只捕获整个匹配结果,对应-o选项 char *current = line; size_t current_len = strlen(line); // 设置编译flags int cflags = REG_EXTENDED; if (options.i) { cflags |= REG_ICASE; } cflags |= REG_NEWLINE; // 对齐grep的换行处理逻辑 // 编译正则表达式 ret = regcomp(®, pattern, cflags); if (ret != 0) { char err_buf[1024]; regerror(ret, ®, err_buf, sizeof(err_buf)); fprintf(stderr, "regex compile error: %s\n", err_buf); regfree(®); return; } if (options.v) { // 可自行实现-v选项的正则逻辑,此处暂略 regfree(®); return; } // 循环匹配所有结果 while (current_len > 0) { ret = regexec(®, current, 1, match, 0); if (ret != 0) { break; // 无更多匹配 } // 计算匹配的起始指针和长度 char *match_start = current + match[0].rm_so; size_t match_len = match[0].rm_eo - match[0].rm_so; // 输出匹配结果 printf("%.*s\n", (int)match_len, match_start); // 移动到下一个匹配起始位置,避免死循环 size_t skip = match[0].rm_eo; if (skip == 0) { skip = 1; // 匹配空字符串时强制跳过1个字符 } current += skip; current_len -= skip; } // 释放正则资源 regfree(®); }
关键注意事项
- 必须调用
regfree()释放regex_t结构体,避免内存泄漏 - 处理空匹配情况:当
match[0].rm_eo == match[0].rm_so时,需手动跳过1个字符,防止死循环 - 错误处理:
regcomp()和regexec()的返回值需检查,用regerror()可获取可读错误信息
内容的提问来源于stack exchange,提问作者Liuba
相关产品推荐
相关产品推荐

