(C语言进阶)strstr

内容重点:

重点介绍处理字符和字符串的库函数的使用和注意事项

 下面是将要介绍的几个函数:

  • 求字符串长度 strlen
  • 长度不受限制的字符串函数 strcpy strcat  strcmp
  • 长度受限制的字符串函数介绍 strncpy strncat strncmp
  • 字符串查找 strstr(本节)strtok
  • 错误信息报告 strerror
  • 字符操作
  • 内存操作函数 memcpy memmove memset memcmp

本节内容: 

  • strstr
  • strchr
  • strrchr
  • 模拟实现strstr

strstr

char * strstr ( const char *str1, const char * str2);

译文:

定位子字符串
返回指向str1中第一个出现的str2的指针,如果str2不是str1的一部分,则返回null指针。
匹配过程不包括终止的null字符,但仅限于此。

返回值

译文:

指向在str2中指定的整个字符序列的str1中第一个出现的指针,如果该序列不在str1中,则为空指针

简单来说:在str1中找str2出现的位置,返回str2的字符串在str1字符串第一次出现的位置。如果找不到返回空指针。

1.使用示例

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include<string.h>
 
int main()
{
	char arr1[] = "abcdebcdef";
	char arr2[] = "bcd";
	char* p = strstr(arr1, arr2);
	if (p == NULL)
	{
		printf("找不到");
	}
	else
	{
		printf("%s\n", p);
	}
	return 0;
}

输出结果:


strchr

const char * strchr ( const char * str, int character );

或者

char * strchr ( const char * str, int character ); 

译文:

查找字符串中第一个出现的字符
返回一个指针,指向C字符串str中第一个出现的字符。
终止的null字符被认为是C字符串的一部分。因此,也可以定位它,以便检索指向字符串末尾的指针。

用法:与strstr 函数比较类似的函数,用来找出一个字符在一个字符串中第一次出现的位置

使用示例 

例1

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include<string.h>
 
int main()
{
	char arr1[] = "abcdebcdef";
	char str = 'e';
	char* p = strchr(arr1, str);
	if (p == NULL)
	{
		printf("找不到");
	}
	else
	{
		printf("%s\n", p);
	}
	return 0;
}

输出结果:


strrchr

const char * strrchr ( const char * str, int character );  

或者

char * strrchr ( char * str, int character );

译文:

查找字符串中最后一个出现的字符
返回一个指针,指向C字符串str中最后一个出现的字符。
终止的null字符被认为是C字符串的一部分。因此,也可以定位它来检索指向字符串末尾的指针。

用法:用来找出一个字符在一个字符串最后出现的位置

使用示例

例题1

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include<string.h>

int main()
{
	char arr1[] = "abcdebcdef";
	char str = 'e';
	char* p = strrchr(arr1, str);
	if (p == NULL)
	{
		printf("找不到");
	}
	else
	{
		printf("%s\n", p);
	}
	return 0;
}

输出结果: 


模拟实现strstr

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include<string.h>
 
char* my_strstr(const char* str1, const char* str2)
{
	char* s1 = NULL;
	char* s2 = NULL;
	char* cp = (char*)str1;//cp作为标记
	while (*cp)
	{
		s1 = cp;
		s2 = (char*)str2;
		while (*s1 && *s2 && *s1 == *s2)//s1空为没有匹配项,s2为空则匹配成功
		{
			s1++;
			s2++;
		}
		if (*s2 == '\0')
		{
			return cp;//为了判断是否找到了,找到了直接跳出
		}
		cp++;
	}
	return NULL;//没有找到返回空指针
}
 
int main()
{
	char arr1[] = "abbbcdef";
	char arr2[] = "bbc";
	char* p = my_strstr(arr1, arr2);
	if (p == NULL)
	{
		printf("找不到\n");
	}
	else
	{
		printf("%s\n", p);
	}
	return 0;
}

输出结果:

解释:

在对该代码进行解释之前,我们先用其他例子来进行说明,便于代码的理解。

示例1 :当arr1[]=“abcdebcdf”,arr2[]=“bcd”时

a与b不匹配,指向arr1开头的指针需要移动到下一个字符b,而arr2的指针不需要移动。之后继续操作,b与b相等,c与c相等,d与d相等,因此arr1和arr2匹配上了,就此结束。

首先我们必须先需要对两个指针进行比较,即先创建*str1和*str2两个指针并且都分别指向两个数组的开头位置。分别对两个指针指向内容来进行匹配即可

但是如果出现

示例2:当arr1[]=“abbbcdeff”,arr2[]=“bbc”时

顺着刚刚的思路,a与b不匹配,指向arr1开头的指针需要移动到下一个字符b,而arr2的指针不需要移动。之后继续操作,arr1的第一个b与arr2的b匹配上了,arr1的第二个b与第二个b匹配上了,arr1的第三个b与arr2c没有匹配上。但是我们可以看到arr2的bbc是从arr1的第二个b才开始匹配上的。

因此我们需要一个指针记住刚刚的位置,使得s1能够从开始匹配成功时(两个字符相等)能够重新开始与s2的指向内容重新匹配(s1=cp,之后开始匹配),因此引入cp。那么s2也需要重新返回开头重新匹配(s2=str2)。每当两个字符完全不相等(完全不可能匹配上,这里也就是两个数组第一个字符(arr1是a,arr2是b)不匹配的情况。)或者arr2匹配过一轮时,直接让cp++,让s1返回到cp标记下一个位置。

我们看这道题,每当开始匹配成功时,这里也就是第一个b开始匹配上,直到s1为b,s2为c时,此时匹配失败,此时我们需要让s1的位置重新回到开始匹配上的下一个位置(也就是第二个b)那么在之前需要cp++,然后s1=cp;同时让s2回到刚刚的位置,重新开始匹配(s2=str2)

大致我们搞清楚了,因此我们需要先循环匹配while(s1和s2的内容是否相等),同时满足s1,s2的内容不为'\0',s1为'\0'则表面s2没有满足匹配的;s2为'\0',则表面s1与s2已经匹配成功;但是我们需要注意的是整个while的条件,当*cp的指向还有内容时,我们需要继续循环。

当然了一些小细节还是需要多思考一下的,这个函数实现非常的巧妙。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1 愉快的开始-HELLO WORLD 14 1.1 INCLUDE头文件包含 14 1.2 MAIN函数 14 1.3 注释 14 1.4 {}括号,程序题和代码块 14 1.5 声明 14 1.6 C语言自定义名字的要求 15 1.7 PRINTF函数 15 1.8 RETURN语句 15 1.9 SYSTEM系统调用 15 1.9.1 System返回值在windows和unix下的不同, 15 1.9.2 POSIX 15 1.10 C语言编译过程,GCC参数简介 16 1.10.1 C语言编译过程 16 1.10.2 -E预编译 16 1.10.3 -S汇编 16 1.10.4 -c编译 16 1.10.5 链接 16 1.11 操作系统结构 17 1.11.1 用户模式 17 1.11.2 内核模式 17 1.12 64位,32位系统区别 18 1.12.1 CPU内部结构与寄存器 18 1.12.2 RISC与CISC CPU构架 18 1.12.3 SPARC,x86与ARM 18 1.13 汇编语言 18 1.13.1 I386汇编简介 18 1.13.2 VS反汇编 19 1.14 IDE工具 19 1.14.1 QT常用快捷键 19 1.14.2 VS常用快捷键 19 1.14.3 VS断点,调试 19 2 C语言中的数据类型 19 2.1 常量 19 2.1.1 #define 19 2.1.2 const 19 2.2 字符串常量 20 2.3 二进制数、位、字节与字 20 2.4 八进制 20 2.5 十六进制 20 2.6 原码 21 2.7 反码 21 2.8 补码 21 2.9 SIZEOF关键字 22 2.10 INT类型 22 2.10.1 int常量,变量 22 2.10.2 printf输出int值 23 2.10.3 printf输出八进制和十六进制 23 2.10.4 short,long,long long,unsigned int 23 2.10.5 整数溢出 23 2.10.6 大端对齐与小端对齐 23 2.11 CHAR类型 24 2.11.1 char常量,变量 24 2.11.2 printf输出char 24 2.11.3 不可打印char转义符 24 2.11.4 char和unsigned char 25 2.12 浮点FLOAT,DOUBLE,LONG DOUBLE类型 25 2.12.1 浮点常量,变量 25 2.12.2 printf输出浮点数 25 2.13 类型限定 25 2.13.1 const 25 2.13.2 volatile 26 2.13.3 register 26 3 字符串格式化输出和输入 26 3.1 字符串在计算机内部的存储方式 26 3.2 PRINTF函数,PUTCHAR函数 27 3.3 SCANF函数与GETCHAR函数 28 4 运算符表达式和语句 29 4.1 基本运算符 29 4.1.1 = 29 4.1.2 + 29 4.1.3 – 29 4.1.4 * 29 4.1.5 / 29 4.1.6 % 29 4.1.7 += 29 4.1.8 -= 29 4.1.9 *= 29 4.1.10 /= 30 4.1.11 %= 30 4.1.12 ++ 30 4.1.13 -- 30 4.1.14 逗号运算符 30 4.1.15 运算符优先级 30 4.2 复合语句 31 4.3 空语句 31 4.4 类型转化 31 5 条件分支语句 31 5.1 关系运算符 31 5.1.1 < 31 5.1.2 <= 31 5.1.3 > 32 5.1.4 >= 32 5.1.5 == 32 5.1.6 != 32 5.2 关系运算符优先级 32 5.3 逻辑运算符 32 5.3.1 && 32 5.3.2 || 32 5.3.3 ! 33 5.4 IF 33 5.5 IF ELSE 34 5.6 IF ELSE IF 34 5.7 SWITCH与BREAK,DEFAULT 35 5.8 条件运算符? 36 5.9 GOTO语句与标号 36 6 循环语句 36 6.1 WHILE 36 6.2 CONTINUE 37 6.3 BREAK 37 6.4 DO WHILE 37 6.5 FOR 37 6.6 循环嵌套 37 7 数组 38 7.1 一维数组定义与使用 38 7.2 数组在内存的存储方式 38 7.3 一维数组初始化 38 7.4 二维数组定义与使用 39 7.5 二维数组初始化 39 8 字符串与字符数组 39 8.1 字符数组定义 39 8.2 字符数组初始化 39 8.3 字符数组使用 40 8.4 随机数产生函数RAND与SRAND 40 8.5 用SCANF输入字符串 40 8.6 字符串的结束标志 41 8.7 字符串处理函数 41 8.7.1 gets 41 8.7.2 fgets函数 41 8.7.3 puts函数 42 8.7.4 fputs函数 42 8.7.5 strlen,字符串长度 42 8.7.6 strcat,字符串追加 42 8.7.7 strncat,字符串有限追加 43 8.7.8 strcmp,字符串比较 43 8.7.9 strncmp,字符串有限比较 43 8.7.10 strcpy字符串拷贝 43 8.7.11 strncpy字符串有限拷贝 43 8.7.12 sprintf,格式化字符串 43 8.7.13 Sscanf函数 44 8.7.14 strchr查找字符 44 8.7.15 strstr查找子串 44 8.7.16 strtok分割字符串 44 8.7.17 atoi转化为int 45 8.7.18 atof转化为float 45 8.7.19 atol转化为long 45 9 函数 45 9.1 函数的原型和调用 45 9.2 函数的形参与实参 45 9.3 函数的返回类型与返回值 46 9.4 MAIN函数与EXIT函数与函数的RETURN语句 46 9.5 多个源代码文件程序的编译 47 9.5.1 头文件的使用 47 9.5.2 #include与#define的意义 47 9.5.3 #ifndef与#endif 47 9.6 函数的递归 48 9.6.1 递归的过程分析 48 9.6.2 递归的优点 52 9.6.3 递归的缺点 52 1 指针 52 1.1 指针 52 1.1.1 指针的概念 52 1.1.2 指针变量的定义 52 1.1.3 &取地址运算符 52 1.1.4 无类型指针 52 1.1.5 NULL 53 1.1.6 空指针与野指针 53 1.1.7 指针的兼容性 53 1.1.8 指向常量的指针与指针常量 54 1.1.9 指针与数组的关系 54 1.1.10 指针运算 54 1.1.11 通过指针使用数组元素 55 1.1.12 指针数组 55 1.1.13 指向指针的指针(二级指针) 55 1.1.14 指向二维数组的指针 57 1.1.15 指针变量做为函数的参数 57 1.1.16 一维数组名作为函数参数 57 1.1.17 二维数组名作为函数参数 58 1.1.18 const关键字保护数组内容 58 1.1.19 指针做为函数的返回值 58 1.1.20 指向函数的指针 59 1.1.21 把指向函数的指针做为函数的参数 60 1.1.22 memset,memcpy,memmove函数 61 1.1.23 指针小结 63 2 字符指针与字符串 64 2.1 指针和字符串 64 2.2 通过指针访问字符串数组 64 2.3 函数的参数为CHAR * 64 2.4 指针数组做为MAIN函数的形参 65 3 内存管理 65 3.1 作用域 65 3.1.1 auto自动变量 65 3.1.2 register寄存器变量 65 3.1.3 代码块作用域的静态变量 66 3.1.4 代码块作用域外的静态变量 66 3.1.5 全局变量 66 3.1.6 外部变量与extern关键字 66 3.1.7 全局函数和静态函数 66 3.2 内存四区 66 3.2.1 代码区 67 3.2.2 静态区 67 3.2.3 栈区 67 3.2.4 栈溢出 68 3.2.5 堆区 68 3.3 堆的分配和释放 70 3.3.1 malloc 70 3.3.2 free 70 3.3.3 calloc: 70 3.3.4 realloc 71 4 结构体,联合体,枚举与TYPEDEF 71 4.1 结构体 71 4.1.1 定义结构体struct和初始化 71 4.1.2 访问结构体成员 71 4.1.3 结构体的内存对齐模式 72 4.1.4 指定结构体元素的位字段 72 4.1.5 结构数组 72 4.1.6 嵌套结构 73 4.1.7 结构体的赋值 73 4.1.8 指向结构体的指针 73 4.1.9 指向结构体数组的指针 73 4.1.10 结构中的数组成员和指针成员 73 4.1.11 在堆中创建的结构体 74 4.1.12 将结构作为函数参数 74 4.1.13 结构,还是指向结构的指针 74 4.2 联合体 75 4.3 枚举类型 75 4.3.1 枚举定义 75 4.3.2 默认值 76 4.4 TYPEDEF 76 4.5 通过TYPEDEF定义函数指针 76 5 文件操作 77 5.1 FOPEN 77 5.2 二进制和文本模式的区别 77 5.3 FCLOSE 78 5.4 GETC和PUTC函数 78 5.5 EOF与FEOF函数文件结尾 78 5.6 FPRINTF,FSCANF,FGETS,FPUTS函数 78 5.7 STAT函数 78 5.8 FREAD和FWRITE函数 79 5.9 FREAD与FEOF 79 5.10 通过FWRITE将结构保存到二进制文件中 79 5.11 FSEEK函数 80 5.12 FTELL函数 80 5.13 FFLUSH函数 80 5.14 REMOVE函数 81 5.15 RENAME函数 81 6 基础数据结构与算法 82 6.1 什么是数据结构 82 6.2 什么是算法 82 6.3 排序 83 6.3.1 冒泡排序 83 6.3.2 选择排序 83 6.4 查找 83 6.4.1 顺序查找 83 6.4.2 二分查找 83 6.5 链表 84 6.5.1 单向链表定义 84 6.5.2 单向链表数据结构定义 85 6.5.3 单向链表的实现 85

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值