strstr函数
strstr函数是C语言中的一个字符串函数,用于在一个字符串中查找另一个字符串的出现位置。
它的函数原型如下:
char *strstr(const char *haystack, const char *needle);
在这个函数中,haystack表示被搜索的字符串,needle表示要查找的子字符串。函数的返回值是一个指针,指向第一次出现needle的位置,如果没有找到,返回NULL。
例如,我们可以使用strstr函数来查找一个字符串中是否包含另一个字符串:
#include <stdio.h>
#include <string.h>int main() {char haystack[] = "Hello, world!";char needle[] = "world";char *result = strstr(haystack, needle);if (result != NULL) {printf("'%s' is found in '%s' at position %ld.\n", needle, haystack, result - haystack);} else {printf("'%s' is not found in '%s'.\n", needle, haystack);}return 0;
}
上述代码输出为:
'world' is found in 'Hello, world!' at position 7.
需要注意的是,strstr函数是区分大小写的,如果要进行大小写不敏感的查找,可以使用其他函数,例如strcasestr。
使用示例
查找子字符串成功后,用strncpy修改内存块内容。
#include <stdio.h>
#include <string.h>
#include<assert.h>int main()
{char str[] = "This is a simple string hello world!";char* pch;pch = strstr(str, "string");if (pch != NULL){printf("%s\n",str);printf("%s\n",pch);strncpy(pch, "sifang",6);}printf("%s\n",str);printf("%s\n",pch);return 0;
}
输出结果:
由输出的结果可知,pch的内容为str的子字符串,指向的地址是第一次相同的位置,也就是说pch指向的内存块是str所指向的内存块。当用strncpy修改pch所指向的内存块值时,其str所指向的对应内存块内容发生改变。
用memcpy替换strncpy,其输出的结果是相同的。
查找子字符成功,strncp的长度修改,引起结果异常。
#include <stdio.h>
#include <string.h>
#include<assert.h>int main()
{char str[] = "This is a simple string hello world!";char* pch;pch = strstr(str, "string");if (pch != NULL){printf("%s\n",str);printf("%s\n",pch);strncpy(pch, "sifang",7);//只是将6修改为7}printf("%s\n",str);printf("%s\n",pch);return 0;
}
输出结果:
对比上面输出的结果可以看出,strncpy后,后面对字符串做了截断处理,这是因为strncpy时,只有sifang六个字符,而长度为7 ,因此将\0的结束字符也拷贝了,引起截断。这种情况和用strcpy的结果是一样的,因为strcpy拷贝自动带上结束符\0。
#include <stdio.h>
#include <string.h>
#include<assert.h>int main()
{char str[] = "This is a simple string hello world!";char* pch;pch = strstr(str, "string");if (pch != NULL){printf("%s\n", str);printf("%s\n", pch);strcpy(pch, "sifang");}printf("%s\n", str);printf("%s\n", pch);return 0;
}
结果
模拟实现
#include <stdio.h>
#include <string.h>
#include <assert.h> char *my_strstr(const char *haystack, const char *needle) { assert(haystack != NULL && needle != NULL); // 确保两个指针都不是NULL if (*needle == '\0') { // 如果needle是空字符串,则认为在haystack的开头就找到了 return (char *)haystack; } while (*haystack != '\0') { const char *h = haystack; const char *n = needle; // 逐个字符比较haystack和needle while (*h != '\0' && *n != '\0' && *h == *n) { h++; n++; } // 如果needle已经完全匹配,返回haystack的起始位置 if (*n == '\0') { return (char *)haystack; } // 否则移动haystack到下一个字符 haystack++; } // 没有找到匹配项 return NULL;
} int main() { const char *haystack = "Hello, world!"; const char *needle = "world"; char *result = my_strstr(haystack, needle); if (result != NULL) { printf("Found '%s' in '%s' at position: %ld\n", needle, haystack, result - haystack); } else { printf("'%s' not found in '%s'.\n", needle, haystack); } return 0;
}
strnstr函数
strnstr函数是C语言标准库中的一个函数,用于在指定长度的字符串中查找第一次出现子串的位置。它的原型通常如下:
char *strnstr(const char *s1, const char *s2, size_t n);
其中:
- s1 是主字符串,即要搜索的字符串。
- s2 是子字符串,即要在 s1 中查找的字符串。
- n 是指定 s1 中参与搜索的最大字符数。
strnstr 函数从 s1 的开头开始,搜索 s2 在 s1 中首次出现的位置,但只搜索到 s1 的前 n 个字符。如果找到 s2,则返回指向 s1 中 s2 首次出现的位置的指针;如果没有找到,则返回 NULL。
这个函数在处理可能包含子串的字符串时非常有用,尤其是当你不确定整个字符串是否都包含在内,或者只关心字符串的一部分时。通过限制搜索的长度,你可以避免不必要的内存访问,并提高性能。
这里是一个简单的例子,演示了如何使用 strnstr 函数:
#include <stdio.h> #include <string.h> int main() { const char *s1 = "Hello, world!"; const char *s2 = "world"; size_t n = 12; // 只搜索 s1 的前 12 个字符 char *result = strnstr(s1, s2, n); if (result != NULL) { printf("Found '%s' in '%s' at position: %ld\n", s2, s1, result - s1); } else { printf("'%s' not found in '%s' (up to %zu characters).\n", s2, s1, n); } return 0; }
在这个例子中,strnstr 函数会在 s1 的前 12 个字符中搜索 s2("world")。因为 "world" 确实存在于这 12 个字符中,所以函数会返回指向 "world" 在 s1 中首次出现的位置的指针,并打印出该位置。如果 "world" 不在这 12 个字符中,或者 s1 本身少于 12 个字符,那么函数将返回 NULL。
模拟实现
strnstr函数的模拟实现可以有多种方式,下面我将提供两种基于不同策略的模拟实现,并加入assert语句来确保输入的有效性:
1. 朴素的循环实现
#include <stdio.h> #include <string.h> #include <assert.h> char *my_strnstr(const char *s1, const char *s2, size_t n) { assert(s1 != NULL && s2 != NULL); // 确保s1和s2都不是NULL if (n == 0) return NULL; // 如果n为0,则无法搜索,直接返回NULL const char *p1 = s1; const char *p2 = s2; while (n--) { if (*p2 == '\0') { // 子串s2已结束,找到了匹配 return (char *)s1; } if (*p1 != *p2) { // 当前字符不匹配,重置p2到s2的开头,p1移动到下一个字符 p2 = s2; if (*p1 == '\0') { // 主串s1已结束,没有找到匹配 return NULL; } p1++; } else { // 当前字符匹配,继续检查下一个字符 p1++; p2++; } } // 搜索完n个字符后仍未找到匹配 return NULL; } int main() { const char *s1 = "Hello, world!"; const char *s2 = "world"; size_t n = strlen(s1); // 测试整个字符串 char *result = my_strnstr(s1, s2, n); if (result != NULL) { printf("Found '%s' in '%s' at position: %ld\n", s2, s1, result - s1); } else { printf("'%s' not found in '%s'.\n", s2, s1); } return 0; }
2. KMP(Knuth-Morris-Pratt)算法实现(简化版)
KMP算法是一种更高效的字符串匹配算法,但由于其实现相对复杂,这里仅提供一个简化的模拟版本。
#include <stdio.h> #include <string.h> #include <assert.h> // 简化的KMP算法实现(未完整实现KMP的所有优化) char *my_strnstr_kmp(const char *s1, const char *s2, size_t n) { assert(s1 != NULL && s2 != NULL); if (n == 0) return NULL; if (*s2 == '\0') return (char *)s1; // 如果s2为空字符串,则s1的任何位置都是匹配点 const char *p1 = s1; const char *p2 = s2; while (n--) { if (*p1 == *p2) { // 字符匹配,继续检查下一个字符 if (*p2 == '\0') { // s2已结束,找到了匹配 return (char *)s1; } p1++; p2++; } else { // 当前字符不匹配,重置p2到s2的开头,p1根据s2的某些性质移动(这里简化处理) p2 = s2; if (*p1 == '\0') { // s1已结束,没有找到匹配 return NULL; } p1++; } } // 搜索完n个字符后仍未找到匹配 return NULL; } int main() { // ... 示例用法与上一个实现相同 ... }
注意:上面的KMP算法实现是一个简化的版本,它没有包含KMP算法中用于优化搜索过程的“部分匹配表”(Partial Match Table, PMT)或“失败函数”(Failure Function)。在实际应用中,完整的KMP算法会构建一个这样的表来加速搜索过程。
strstr和strnstr函数的区别
strstr
和strnstr
函数在C语言中用于字符串处理,但它们在功能和使用上有所不同。
strstr
函数:
- 功能:用于在字符串
str1
中查找子字符串str2
首次出现的位置。- 参数:接受两个字符串参数
str1
和str2
,其中str1
是要被搜索的字符串,str2
是要查找的子字符串。- 返回值:如果找到子字符串
str2
,则返回指向其在str1
中首次出现的位置的指针;如果没有找到,则返回NULL
。- 特点:
strstr
函数会搜索整个str1
字符串以查找str2
,不考虑字符串的长度限制。
strnstr
函数:
- 功能:与
strstr
类似,也是在字符串中查找子串,但允许指定要搜索的最大字符数。- 参数:除了接受两个字符串参数
str1
(主字符串)和str2
(子字符串)外,还接受一个额外的参数n
,表示在str1
中参与搜索的最大字符数。- 返回值:与
strstr
相同,如果找到子字符串str2
,则返回指向其在str1
中首次出现的位置的指针;如果没有找到,则返回NULL
。- 特点:
strnstr
函数只会在str1
的前n
个字符中搜索str2
,而不会搜索整个字符串。这在某些情况下可以提高效率,特别是当你知道子字符串不可能出现在主字符串的某个特定部分之后时。总结来说,
strstr
和strnstr
函数的主要区别在于是否允许指定搜索的最大字符数。如果你需要搜索整个字符串,可以使用strstr
;如果你只想在字符串的某个特定部分中搜索,可以使用strnstr
。