C语言如何在文本文件中搜索排除子串的独立精确关键词
独立整词关键词匹配的实现方案
现有代码的问题定位
- 原代码使用
strstr做无边界的子串匹配,只要行内任意位置出现关键词字符序列就判定为命中,无法过滤掉关键词作为子串嵌入其他单词的场景,实现不了独立整词匹配的目标,比如关键词为a时,cap、macaroni单词里内嵌的a都会被误判为匹配项 - 缺少
fopen操作的返回值校验,当目标文件不存在或无读取权限时,后续的文件读取操作会触发未定义行为 - 读取行的方式用
fscanf的%[^\n]格式,没有可靠的边界保护,超长行可能造成缓冲区溢出
整词匹配的判断规则
要满足题目要求的“关键词为独立单词形式,前后边界为空白字符”的匹配逻辑,当检索到关键词在当前行的出现位置时,必须同时校验两个方向的边界:
- 左边界:关键词的起始位置要么是整行的首个字符,要么紧邻左侧的字符为空白字符(空格、制表符、换行符等)
- 右边界:关键词的结束位置要么是整行的最后一个有效字符,要么紧邻右侧的字符为空白字符
改进后的可运行代码
#include <stdio.h> #include <string.h> #include <ctype.h> #define MAXLINESIZE 1024 #define MAXKEYWORD 256 // 校验匹配位置是否符合独立整词的边界要求 int is_valid_word_boundary(const char *line, const char *match_pos, const char *keyword) { size_t keyword_len = strlen(keyword); // 校验左边界 if (match_pos != line) { // 非行首位置时,左侧必须是空白字符才算合法边界 if (!isspace(*(match_pos - 1))) { return 0; } } // 校验右边界 const char *right_pos = match_pos + keyword_len; if (*right_pos != '\0') { // 非行尾位置时,右侧必须是空白字符才算合法边界 if (!isspace(*right_pos)) { return 0; } } return 1; } int main() { FILE* file = fopen("yankee.txt", "r"); // 增加文件打开失败的异常处理 if (file == NULL) { perror("打开文件失败"); return 1; } int linenumber = 0; const char *keyword = "a"; char keywordline[MAXLINESIZE]; // 用fgets替代原有的fscanf读取行,避免缓冲区溢出 while (fgets(keywordline, MAXLINESIZE, file) != NULL) { linenumber++; // 移除读入行末尾自带的换行符,避免干扰边界判断 keywordline[strcspn(keywordline, "\n")] = '\0'; int matched = 0; const char *search_pos = keywordline; // 遍历整行检索所有可能的关键词出现位置 while ((search_pos = strstr(search_pos, keyword)) != NULL) { // 校验当前匹配位置的边界是否合法 if (is_valid_word_boundary(keywordline, search_pos, keyword)) { matched = 1; break; } // 跳过当前位置,避免循环死锁 search_pos++; } if (matched) { printf("linenumber = %d\n'%s'\n\n", linenumber, keywordline); } } fclose(file); return 0; }
运行效果
针对题目给出的测试文本,上述代码的输出和预期完全一致:
linenumber = 2 'riding on a pony' linenumber = 3 'Stuck a feather in his cap'
代码会自动过滤掉第4行And called it macaroni.中内嵌在macaroni单词里的字母a,也不会把第1行没有独立a出现的内容纳入匹配结果。
内容的提问来源于stack exchange,提问作者YeetCode
相关产品推荐
相关产品推荐

