C语言字符串查找核心函数:strstr与strtok用法及模拟实现
掌握C语言中用于字符串处理的strstr与strtok函数,能够高效完成文本解析任务。本文不仅涵盖两函数的标准用法,更通过代码演示其参数传递规则,并深度解析strstr的内部匹配机制与模拟实现。
strstr 字符串定位函数
strstr 是 C 语言标准库 <string.h> 中用于查找子字符串的核心函数。它的基本功能是在一个长字符串中定位指定子串的首次出现位置。
函数原型如下:
char *strstr( const char *string, const char *strCharSet );
返回值说明:
- 如果
strCharSet(目标子串)存在于string(源字符串)中,函数返回指向该子串在源字符串中第一次出现位置的指针。 - 如果不存在,返回
NULL(空指针)。 - 特殊情况下,如果
strCharSet指向空字符串(长度为 0),函数直接返回源字符串string本身。
由于返回值是指针类型,我们在接收结果时通常也需要定义一个指针变量。下面通过代码示例来观察其行为。
第1步:正常查找子串
当目标子串确实存在时,我们可以利用返回值进行后续操作,例如打印从该位置开始的字符串内容。
#include <stdio.h>
#include <string.h>
int main()
{
char arr1[] = "abbbcdbbcef";
char arr2[] = "bbc";
// ret 保存找到的子串起始位置的指针
char* ret = strstr(arr1, arr2);
if (ret == NULL)
{
printf("找不到\n");
}
else
{
// 打印从匹配位置开始到结束的部分
printf("%s\n", ret);
}
return 0;
}
第2步:处理未找到目标的情况
调整输入数据,使目标子串在原字符串中不存在。此时函数应返回空指针,程序逻辑需对此进行处理。
// ... 省略部分代码 ... char arr2[] = "bbcq"; // 假设要查找包含 q 的子串 char* ret = strstr(arr1, arr2); // ...
模拟实现 strstr
理解库函数的行为有助于更高效地使用它,而模拟实现则能揭示其底层逻辑。strstr 的实现涉及指针的移动与回退机制,这也是面试和算法学习中常见的考点。
核心逻辑分析
在逐个字符比对的过程中,主要面临两种情况:
情况一:字符连续匹配成功
这是理想路径。当主串当前字符与子串首字符相等时,两者同时向后移动。若一直匹配直到子串结束符 '\0',则证明匹配成功,返回当前位置。
情况二:中途匹配失败
这是容易出错的点。假设我们同时在比较主串和子串的字符,当发现某一位不相等时:
- 子串的指针需要重置到子串的首地址,重新开始尝试。
- 主串的指针不能简单跳过,而是要回到本次尝试开始位置的下一个字符,重新发起匹配。
为了保证不影响原始的主串遍历入口,我们需要引入临时变量来维护当前的比对位置,并保留一个记录“本轮匹配起点”的哨兵指针。
模拟实现代码
以下是完整的 my_strstr 实现逻辑,包含断言检查和空串保护:
#include <string.h>
#include <assert.h>
char* my_strstr(const char* str1, const char* str2)
{
// 确保传入指针有效
assert(str1 && str2);
// 如果子串为空,直接返回主串首地址
if (*str2 == '\0')
{
return (char*)str1;
}
const char* s1 = NULL;
const char* s2 = NULL;
// cp 用于记录每一轮比对在主串中的起始位置
const char* cp = str1;
while (*cp)
{
// 初始化本轮比对:s1 指向当前起始位,s2 指向子串头
s1 = cp;
s2 = str2;
// 当主串没结束、子串没结束、且对应字符相等时,持续向后比对
while (*s1 !='\0' && *s2!='\0' && *s1 == *s2)
{
s1++;
s2++;
}
// 关键点:如果 s2 到达了 '\0',说明子串全部匹配成功
if (*s2 == '\0')
{
return (char*)cp; // 返回成功的起始位置
}
// 当前位匹配失败,主串起始位置向后移一位,准备下一轮
cp++;
}
return NULL; // 遍历完仍未找到
}strtok 字符串分割函数
在处理日志、配置文件或 CSV 数据时,经常需要将长字符串按特定符号切割成若干片段。此时 strtok 是最常用的工具。
工作原理与特性
函数原型:
char *strtok( char *strToken, const char *strDelimit );
它的工作机制非常独特,甚至可以说具有“副作用”:
- 破坏性修改:
strtok会在每次找到一个标记后,用'\0'替换分隔符,从而切断字符串。因此,被操作的字符串必须是可修改的(如数组拷贝),绝不能操作只读字符串常量。 - 状态保持:函数内部会保存当前正在处理的字符串的剩余部分位置。首次调用需要传入待分割的字符串;随后的调用只需传入
NULL,函数即可基于上次的状态继续寻找下一个标记。 - 多分隔符支持:
strDelimit是一个字符集合,字符串中包含的任何属于该集合的字符都会被视为分隔符。
实战:分割邮箱地址
假设要将邮箱 superverybest@outlooks.net 按照 @ 和 . 进行拆分。
#include <stdio.h>
#include <string.h>
int main()
{
char arr[] = "superverybest@outlooks.net";
const char* sep = "@."; // 定义分隔符集合
char buf[50] = { 0 };
// 注意:strtok 会修改原字符串,所以先拷贝一份
strcpy(buf, arr);
char* ret = NULL;
// 循环获取每一个分片
// 第一次传入 buf,之后传入 NULL
for (ret = strtok(buf, sep); ret != NULL; ret=strtok(NULL, sep))
{
printf("%s\n", ret);
}
return 0;
}
总结
strstr 侧重于内容的精准检索,理解其指针回溯机制能有效避免手动实现时的逻辑漏洞;strtok 侧重于文本的结构化解析,使用时务必牢记其“就地修改”的特性,提前备份字符串即可安全使用。以上就是C语言进阶之字符串查找库函数详解的详细内容,更多关于C语言字符串操作技巧的资料请关注本站其它相关文章!
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









