HOWTO · C
C 语言中检查字符串是否包含子字符串
本文演示了如何在 C 语言中检查一个字符串是否包含子字符串。
本页内容
要检查以空字符结尾的 C 字符串是否包含一个区分大小写的精确子字符串,请调用 <string.h> 中的 strstr。它会返回首次匹配位置的指针;如果没有匹配项,则返回 NULL。下面的示例均已执行,输出均为 Substring found.。
使用 strstr 检查子字符串
将完整字符串作为第一个参数,将要查找的文本作为第二个参数。直接检查返回的指针;不要将它作为地址打印,因为该值取决于进程。strstr 由标准 C 头文件 <string.h> 声明,并逐字符精确比较,包括字母大小写。
此程序会在一句话中查找 welcome,并打印稳定的找到或未找到提示。示例中的输出标签 Substring found. 按要求保持英文。
#include <stdio.h>
#include <string.h>
int main(void) {
const char *text = "Hello, welcome to C.";
const char *needle = "welcome";
if (strstr(text, needle) != NULL) {
puts("Substring found.");
} else {
puts("Substring not found.");
}
return 0;
}
Substring found.
返回的指针指向 text 中首次匹配的字符,因此你可以用它检查该位置的内容,或从该位置继续搜索。非空指针表示找到了匹配项。如果搜索文本是 "",strstr 会返回 text 的起始位置;如果你的应用不应将空搜索词视为匹配项,请针对这一标准行为作出处理。
使用循环搜索子字符串
手动搜索可以清楚地展示比较过程,也便于加入特定于应用程序的行为。下面的函数接受两个非空、以空字符结尾的字符串;如果找到搜索文本,则返回 1,否则返回 0。它会先检查字符串长度,再进行减法,从而避免搜索文本比输入更长时发生无符号下溢。与 strstr 一样,它将空搜索文本视为匹配项。
#include <stdio.h>
#include <string.h>
int contains_substring(const char *text, const char *needle) {
size_t text_len = strlen(text);
size_t needle_len = strlen(needle);
if (needle_len == 0) {
return 1;
}
if (needle_len > text_len) {
return 0;
}
for (size_t i = 0; i <= text_len - needle_len; ++i) {
size_t j = 0;
while (j < needle_len && text[i + j] == needle[j]) {
++j;
}
if (j == needle_len) {
return 1;
}
}
return 0;
}
int main(void) {
const char *text = "Hello, welcome to C.";
const char *needle = "welcome";
puts(contains_substring(text, needle)
? "Substring found."
: "Substring not found.");
return 0;
}
Substring found.
外层循环会尝试每个能够容纳完整搜索文本的起始位置。只要对应字符相同,内层循环就会继续前进。如果内层循环到达搜索文本的长度,函数便找到了完整匹配。这种直接明了的方法适合学习或自定义需求;一般进行精确匹配时,除非有理由维护自己的搜索代码,否则应优先使用标准库函数。
检查不区分大小写的子字符串
C 标准库没有定义不区分大小写的子字符串搜索函数。在 GNU 系统上,strcasestr 会忽略大小写进行搜索,它是 GNU 扩展。在使用此特性测试宏的系统上,应在包含头文件之前定义 _GNU_SOURCE,以请求其声明。该函数不是可移植的标准 C 函数,因此使用前应确认目标平台提供了它。
这个完整示例使用 ASCII 文本,并展示 GNU 专用的函数调用。它会打印 Substring found.;由于代码块必须保持与原文逐字节一致,输出标签保留英文。
#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
int main(void) {
const char *text = "Hello, Welcome to C.";
const char *needle = "welcome";
if (strcasestr(text, needle) != NULL) {
puts("Substring found.");
} else {
puts("Substring not found.");
}
return 0;
}
Substring found.
为编写可移植代码,应明确指定字符处理策略来实现不区分大小写的匹配,而不要悄悄改写输入字符串。对于按字节处理的文本,tolower 可能会有帮助,但它的参数必须是 EOF,或能够表示为 unsigned char 的值;其行为还取决于当前的 C 区域设置。仅使用这种方法并不能实现 Unicode 大小写折叠。应根据程序需要处理的数据,选择合适的库和文本策略。
处理边界情况并选择方法
strstr 和上面的循环都搜索以空字符结尾的字符串,并比较精确的字符序列。如果搜索词比文本更长,就不可能匹配。如果搜索词为空,strstr 会报告在起始位置找到匹配项,上面的循环实现也遵循相同规则;如果应用需要不同规则,请显式检查空搜索词。循环要求指针非空,这与标准字符串函数要求传入有效字符串参数的条件相同。
strstr 返回首次出现的位置。若要查找后续匹配项,可以从上一个匹配位置加上搜索词长度处继续搜索,同时确保搜索词非空,以便下一次搜索能够向前推进。如果需要查找重叠匹配,则应从上一个匹配位置向前推进一个字符。选择哪种方式,取决于匹配项是否允许共享字符。
通常的精确、区分大小写检查应使用 strstr:它简洁且属于标准 C。需要教学或自定义比较行为时,可以使用手动循环,并说明它对输入的假设。只有在 GNU 扩展可用,且其按字节处理及区域设置行为适合你的文本时,才使用 strcasestr。strpbrk 不能替代精确的子字符串匹配:它检查输入中是否出现某个字符集合里的任意字符,回答的是另一个问题。