Instr 函数的核心功能是:在一个字符串(源字符串)中查找另一个字符串(子字符串)第一次出现的位置。

(图片来源网络,侵删)
在 C 语言标准库中,并没有一个直接叫 instr 的函数,我们可以使用标准库中强大的字符串处理函数来轻松实现它,最常用的就是 strstr。
使用标准库函数 strstr (最推荐)
strstr 是 C 标准库 <string.h> 中的一个函数,它的原型是:
char *strstr(const char *haystack, const char *needle);
haystack: 源字符串,即被搜索的字符串。needle: 子字符串,即要查找的字符串。
返回值:
- 如果找到
needle,strstr返回一个指向haystack中needle首次出现位置的指针。 - 如果没有找到,
strstr返回NULL。
如何将指针转换为“位置” (索引)?
Instr 通常返回一个从 1 开始的整数索引,而 strstr 返回的是指针,我们可以通过计算指针与源字符串首地址的差值来得到从 0 开始的索引,+1 即可得到从 1 开始的索引。
示例代码:实现 my_instr 函数
下面我们封装一个自己的 my_instr 函数,使其行为与 VB 的 Instr 函数一致。
#include <stdio.h>
#include <string.h> // 必须包含这个头文件
/**
* @brief 模拟 VB 的 Instr 函数
* @param src_str 源字符串
* @param sub_str 要查找的子字符串
* @return 如果找到,返回子字符串在源字符串中首次出现的起始位置 (从1开始);
* 如果未找到或源字符串为空,返回 0。
*/
int my_instr(const char *src_str, const char *sub_str) {
// 1. 检查源字符串或子字符串是否为空指针
if (src_str == NULL || sub_str == NULL) {
return 0;
}
// 2. 使用 strstr 查找子字符串
const char *pos = strstr(src_str, sub_str);
// 3. 判断是否找到
if (pos != NULL) {
// 找到了,计算位置
// pos - src_str 得到从 0 开始的偏移量
// +1 使其从 1 开始,符合 Instr 的习惯
return (int)(pos - src_str) + 1;
} else {
// 没找到,返回 0
return 0;
}
}
int main() {
const char *text = "Hello, welcome to the world of C programming!";
const char *word1 = "world";
const char *word2 = "C";
const char *word3 = "Python"; // 不存在的单词
// 查找 "world"
int pos1 = my_instr(text, word1);
printf("'%s' found at position: %d\n", word1, pos1); // 应该输出 21
// 查找 "C"
int pos2 = my_instr(text, word2);
printf("'%s' found at position: %d\n", word2, pos2); // 应该输出 31
// 查找不存在的 "Python"
int pos3 = my_instr(text, word3);
printf("'%s' found at position: %d\n", word3, pos3); // 应该输出 0
// 测试子字符串在开头的情况
const char *text2 = "apple banana";
int pos4 = my_instr(text2, "apple");
printf("'%s' found at position: %d\n", "apple", pos4); // 应该输出 1
// 测试子字符串就是源字符串本身的情况
int pos5 = my_instr(text2, text2);
printf("'%s' found at position: %d\n", text2, pos5); // 应该输出 1
return 0;
}
代码解释:
#include <string.h>: 引入strstr函数所在的头文件。my_instr函数:- 参数是
const char*,表示我们不打算修改这些字符串,符合strstr的用法。 - 首先进行空指针检查,这是一个好习惯。
- 调用
strstr进行查找。 - 根据
strstr的返回值进行判断。 (pos - src_str)是指针算术,它计算了两个地址之间的差值,这个差值正好是子字符串在源字符串中的起始索引(从 0 开始)。+ 1将其转换为从 1 开始的索引。- 如果没找到,返回 0。
- 参数是
手动实现字符串查找 (不使用 strstr)
为了更深入地理解字符串查找的原理,我们可以不使用 strstr,而是自己用循环来实现这个逻辑,这有助于理解底层算法。
基本思路:
- 遍历源字符串的每一个字符,作为可能的匹配起点。
- 对于每一个起点,检查从这个位置开始的后续字符是否与子字符串完全匹配。
- 如果完全匹配,就返回当前起点的位置。
- 如果遍历完整个源字符串都没有找到,则返回 0。
示例代码:手动实现 my_instr_manual
#include <stdio.h>
/**
* @brief 手动模拟 Instr 函数,不使用 strstr
* @param src_str 源字符串
* @param sub_str 要查找的子字符串
* @return 如果找到,返回子字符串在源字符串中首次出现的起始位置 (从1开始);
* 如果未找到或源字符串/子字符串为空,返回 0。
*/
int my_instr_manual(const char *src_str, const char *sub_str) {
if (src_str == NULL || sub_str == NULL || *sub_str == '\0') {
// 如果任一字符串为空指针,或者子字符串为空字符串,返回 0
return 0;
}
int i = 0; // 遍历源字符串的索引
while (src_str[i] != '\0') {
int j = 0;
// 检查从 src_str[i] 开始是否匹配 sub_str
while (src_str[i + j] == sub_str[j] && sub_str[j] != '\0') {
j++;
}
// j 移动到了 sub_str 的末尾,说明完全匹配
if (sub_str[j] == '\0') {
return i + 1; // 返回从 1 开始的位置
}
i++; // 继续检查源字符串的下一个字符作为起点
}
// 遍历完源字符串都没找到
return 0;
}
int main() {
const char *text = "Hello, welcome to the world of C programming!";
const char *word1 = "world";
const char *word2 = "C";
int pos1 = my_instr_manual(text, word1);
printf("Manual search: '%s' found at position: %d\n", word1, pos1);
int pos2 = my_instr_manual(text, word2);
printf("Manual search: '%s' found at position: %d\n", word2, pos2);
return 0;
}
代码解释:
- 外层
while循环遍历源字符串src_str。 - 内层
while循环尝试匹配子字符串sub_str。 src_str[i + j] == sub_str[j]比较当前字符是否相等。sub_str[j] != '\0'确保我们不会越界读取sub_str。- 如果内层循环因为
sub_str[j]到达末尾 ('\0') 而退出,说明匹配成功,返回i + 1。 - 如果内层循环因为字符不匹配而退出,外层循环的
i会增加,继续下一次尝试。
总结与对比
| 特性 | strstr (方法一) |
手动实现 (方法二) |
|---|---|---|
| 优点 | 简洁、高效、标准,由 C 标准库提供,通常经过高度优化。 | 有助于理解算法,不依赖特定库,适合学习。 |
| 缺点 | 返回的是指针,需要额外计算才能得到索引。 | 代码复杂,容易出错,性能可能不如标准库实现。 |
| 适用场景 | 实际开发中首选,代码清晰,可读性高。 | 学术练习、面试、或者在不允许使用 string.h 的极端环境中。 |
在日常的 C 语言编程中,你应该优先使用 strstr 来实现 Instr 的功能,它更简洁、更安全,并且性能更有保障,手动实现的方法则更多地用于学习和理解字符串匹配的基本原理。
