0
点赞
收藏
分享

微信扫一扫

【深挖字符串操作】·万字总结,这些知识点你真的懂了吗?

青青子衿谈育儿 2022-04-13 阅读 79

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBARHVvbmku,size_20,color_FFFFFF,t_70,g_se,x_16


 开始前言



介绍完啦!那么接下来我们就......

发车!


目录

 开始前言

一、字符串拷贝操作

(一)、strcpy函数 · (无限制)字符串拷贝

(1)strcpy函数原型解析

(2)strcpy函数的使用

(3)strcpy函数的模拟实现

(4)strcpy函数的缺陷

(二)、strncpy函数 · (有限制)字符串拷贝

(1)strncpy函数原型解析

(2)strncpy函数的使用

(3)strncpy函数的模拟实现

二、字符串追加操作

(一)、strcat函数·(无限制)字符串追加

 (1)strcat函数原型解析

(2)strcat函数的使用

(3)strcat函数的模拟实现

(5)strcat函数的缺陷

(二)、strncat函数·(有限制)字符串追加

(1)strncat函数原型解析

(2)strncat函数的使用

(3)strncat函数的模拟实现

三、字符串比较操作

(一)、strcmp函数·(无限制)字符串比较

(1)strcmp函数原型解析

(2)strcmp函数的使用

(3)strcmp函数的模拟实现

(4)strcmp函数的缺陷

(二)、strncmp函数·(有限制)字符串比较

(1)strncmp函数原型解析

(2)strncmp函数的使用

(3)strncmp函数的模拟实现

四、子串判断

(1)strstr函数原型解析

(2)strstr函数的使用

(3)strstr函数的模拟实现

五、字符串分割操作

(1)strtok函数原型解析

(2)strtok函数的使用

六、预警函数

(1)strerror函数

(2)perror函数

七、最后


寄语


此篇博客是对字符串操作知识点的一个总结。字符串是一个特殊的存在,因为它不属于任何数据类型,因此,很多人在初次接触字符串相关操作的题目时,都不由得会无从下手。

为了确保知识点的完整性和具有实践性,我将会对每个知识点进行图演,与特殊情况解析,以便小伙伴们理解!


一、字符串拷贝操作

在各大笔试题目中,我们总能看到字符串的身影,而字符串的拷贝操作,则是考察我们对字符串特性熟悉与否的一个基本考点。

在第一节中,我们的目标是要弄清strcpy函数与strncpy函数的区别,以及各自的特性,并且能够实现他们,利用此知识点去解决例题!


 (一)、strcpy函数 · (无限制)字符串拷贝

首先,我们得大体弄清strcpy函数是干嘛的:


(1)strcpy函数原型解析


(2)strcpy函数的使用


情境一:

int main()
{
	char vate1_char[] = "Sorrowful life";
	char vate2_char[] = "Happiness";
	char* ret = strcpy(vate1_char, vate2_char);
	printf("%s\n", ret);
	return 0;
}

结果:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBARHVvbmku,size_20,color_FFFFFF,t_70,g_se,x_16


情境二:

int main()
{
	char vate1_char[] = "Happiness";
	char vate2_char[] = "Sorrowful life";
	char* ret = strcpy(vate1_char, vate2_char);
	perror("strcpy:");
	printf("输出:%s\n", ret);
	return 0;
}

结果: 

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBARHVvbmku,size_20,color_FFFFFF,t_70,g_se,x_16


情境一分析:

情境二分析:


(3)strcpy函数的模拟实现

模拟实现:

char* my_strcpy(char* vate1_char, const char* vate2_char)
{
	assert(vate1_char && vate2_char);
	char* temp = vate1_char;
	while (*vate1_char = *vate2_char)//当vate1_char为NULL时停止拷贝
	{
		vate1_char++;
		vate2_char++;
	}
	return temp;
}

int main()
{
	char vate1_char[20] = "我爱编程";
	char vate2_char[] = "编程爱我!";
	char* ret = my_strcpy(vate1_char, vate2_char);
	printf("%s", ret);
	return 0;
}

结果:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBARHVvbmku,size_20,color_FFFFFF,t_70,g_se,x_16


(4)strcpy函数的缺陷


(二)、strncpy函数 · (有限制)字符串拷贝

strncpy函数与strcpy函数不能说毫无差别,只能说功能一模一样。唯一不同的是加入了:限制数

这也使得strcpy函数的弊端得到了根本优化!


(1)strncpy函数原型解析


(2)strncpy函数的使用


情境一:

//1、如果限制数len的数比源字符串的元素位数大,结果是如何?
int main()
{
	char vate1_char[15] = "wwwwwwwwwwwwww";
	char vate2_char[] = "ssssss";
    int len = 10;
	char* ret = strncpy(vate1_char, vate2_char, len);//10为限制数
	printf("%s", ret);
	return 0;
}

结果:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBARHVvbmku,size_20,color_FFFFFF,t_70,g_se,x_16


情境二:

//2、如果源字符串的元素位数比限制数len的数大,结果是如何?
int main()
{
	char vate1_char[15] = "wwwwwwwwwwwwww";
	char vate2_char[] = "ssssss";
	int len = 3;
	char* ret = strncpy(vate1_char, vate2_char, len);
	printf("%s", ret);
	return 0;
}

结果:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBARHVvbmku,size_20,color_FFFFFF,t_70,g_se,x_16


 情境三:

int main()
{
	char vate1_char[15] = "wwwwwwwwwwwwww";
	char vate2_char[] = "ssssss";
	int len = 20;
	char* ret = strncpy(vate1_char, vate2_char, len);
	printf("%s", ret);
	return 0;
}

 结果:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBARHVvbmku,size_20,color_FFFFFF,t_70,g_se,x_16


 情境一分析:

vate1_char数组内部元素摆放:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBARHVvbmku,size_20,color_FFFFFF,t_70,g_se,x_16


 情境二分析:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBARHVvbmku,size_20,color_FFFFFF,t_70,g_se,x_16

vate1_char数组内部元素摆放:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBARHVvbmku,size_20,color_FFFFFF,t_70,g_se,x_16


 情境三分析:




(3)strncpy函数的模拟实现

上代码:

char* my_strncpy(char* v_1, const char* v_2, int len)
{
	assert(v_1 && v_2);//断言
	char* ret = v_1;//将v_1首元素复制给*ret,用于返回值
	while (len--)//交换一次限制数就--
	{
		*v_1++ = *v_2++;//拷贝
	}
	return ret;
}

int main()
{
	char vate_1[25] = "我爱熬夜!也爱学习!";
	char vate_2[] = "我爱生活!";
	int len = 0;
	puts("请输入拷贝数:");
	scanf("%d", &len);
	char* ret = my_strncpy(vate_1, vate_2, len);
	printf("%s\n", ret);
	return 0;
}

结果:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBARHVvbmku,size_20,color_FFFFFF,t_70,g_se,x_16


二、字符串追加操作


(一)、strcat函数·(无限制)字符串追加


图演:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBARHVvbmku,size_20,color_FFFFFF,t_70,g_se,x_16


watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBARHVvbmku,size_20,color_FFFFFF,t_70,g_se,x_16



 (1)strcat函数原型解析


(2)strcat函数的使用


情境一:

上代码:

int main()
{
	char vate_1[20] = { 'h','a','p','p','y'};//字符数组
	char vate_2[] = { 'l','i','f','e'};
	char* ret = strcat(vate_1, vate_2);
	printf("%s\n", ret);
	return 0;
}

结果:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBARHVvbmku,size_20,color_FFFFFF,t_70,g_se,x_16


上代码:

int main()
{
	char vate_1[20] = { 'h','a','p','p','y','\0'};//在字符数组尾部添加'\0'
	char vate_2[] = { 'l','i','f','e','\0'};
	char* ret = strcat(vate_1, vate_2);
	printf("%s\n", ret);
	return 0;
}

结果:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBARHVvbmku,size_20,color_FFFFFF,t_70,g_se,x_16


情境二:

上代码:

int main()
{
	char vate_1[20] = "happy";
	char vate_2[] = "life";
	char* ret = strcat(vate_1, vate_1);
	printf("%s\n", ret);
	return 0;
}

结果:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBARHVvbmku,size_20,color_FFFFFF,t_70,g_se,x_16



情境1解析:


情境2解析:


(3)strcat函数的模拟实现

上代码:

//strcat模拟实现
char* my_strcat(char* value1_char, const char* value2_char)
{
	assert(value1_char && value2_char);
	char* temp = value1_char;//储存目标字符串的首地址
	while (*value1_char)//寻找目标字符串'\0'
	{
		value1_char++;
	}
	while (*value1_char++ = *value2_char++)//在'\0'位置进行源字符串的拷贝
	{
		;
	}
	return temp;//返回
}

int main()
{
	char value1_char[50] = "我热爱生活!也热爱学习!";
	char value2_char[] = "愿世间充满欢乐!";
	char* temp = my_strcat(value1_char, value2_char);
	printf("%s", temp);
	return 0;
}

//理解及思路
//1、保证目标字符串及源字符串的结尾都具备有‘\0’。
//2、目标字符串空间须是可修改的,且是足够大的。
//3、第一步需要找到目标字符串的‘\0’处。
//4、第二步,在目标字符串‘\0’处进行进行拷贝,将源字符串拷贝到目标字符串之后,直到'\0'结束。
//5、需要对目标字符串进行存地址,否则贸然返回目标字符串,所得到的将是尾部'\0\的地址,从而导致空白打印。

(5)strcat函数的缺陷


(二)、strncat函数·(有限制)字符串追加


(1)strncat函数原型解析


(2)strncat函数的使用


情境一:           

上代码:

//strncat函数可以作用于同一个参数吗?
int main()
{
	char vate_1[20] = "joy";
	char vate_2[] = "and sadness";
	int count = 0;
	puts("请输入追加数:");
	scanf("%d", &count);//限制数输入:4
	char* ret = strncat(vate_1, vate_1, count);
	printf("%s\n", ret);
	return 0;
}

结果:


情境二:


(限制数大于源字符串位数)上代码:

//限制数大于源字符串位数,会发生什么?
int main()
{
	char vate_1[20] = "joy";
	char vate_2[] = " and sadness";
	int count = 0;
	puts("请输入追加数:");
	scanf("%d", &count);//限制数输入值:50
	char* ret = strncat(vate_1, vate_2, count);
	printf("%s\n", ret);
	return 0;
}

结果:


 (限制数小于源字符串位数)上代码:

//限制数小于源字符串位数,会发生什么?
int main()
{
	char vate_1[20] = "joy";
	char vate_2[] = " and sadness";
	int count = 0;
	puts("请输入追加数:");
	scanf("%d", &count);//限制数输入值:3
	char* ret = strncat(vate_1, vate_2, count);
	printf("%s\n", ret);
	return 0;
}

结果:



情境一解析:


情境二解析:


(3)strncat函数的模拟实现

上代码:

//strncat函数模拟实现
char* my_strncat(char* v_1, const char* v_2, size_t count)
{
	assert(v_1 && v_2);//断言判断是否为空指针
	char* ret = v_1;//将目标字符串首地址赋值给字符指针:ret用作返回值
	while (*v_1)
	{
		v_1++;//找到目标字符串参数中的'\0'(找到:入口)
	}
	while ((count--))//限制数为0时停止
	{
		if ((*v_1++ = *v_2++) == '\0')
        //当v_1为'\0'时返回指针。
        //此情况是在两个参数都为NULL字节结束的情况下发生
		{
			return ret;
		}
	}
	*v_1 = '\0';
    //而在末尾补NULL字节是在源字符序结尾不为NULL字节时,或同一参数追加时进行。
	return ret;
}
int main()
{
	char vate_1[25] = "道阻且长,";
	char vate_2[] = "行则将至!";
    char* ret = my_strncat(vate_1, vate_2, 100);
	printf("%s\n", ret);
	return 0;
}

结果:


三、字符串比较操作


(一)、strcmp函数·(无限制)字符串比较


(1)strcmp函数原型解析


(2)strcmp函数的使用


上代码:

//strcmp函数能否比对汉字的大小?
//张三与李三
int main()
{
	char v_1[] = "张三";
	char v_2[] = "李三";
	int ret = strcmp(v_1, v_2);
	printf("%d\n", ret);
	return 0;
}

结果:


//strcmp函数能否比对汉字的大小?
//李三与张三
int main()
{
	char v_1[] = "李三";
	char v_2[] = "张三";
	int ret = strcmp(v_1, v_2);
	printf("%d\n", ret);
	return 0;
}

结果:



//strcmp函数能否比对汉字的大小?
//张三与张三
int main()
{
	char v_1[] = "张三";
	char v_2[] = "张三";
	int ret = strcmp(v_1, v_2);
	printf("%d\n", ret);
	return 0;
}

结果:



(3)strcmp函数的模拟实现

上代码:

//strcmp比较字符串
int my_strcmp(const char* value1_char, const char* value2_char)
{
	assert(value1_char && value2_char);//断言
	while (*value1_char == *value2_char)//当两位数比较不同时退出
	{
		if (*value1_char == '\0' && *value2_char == '\0')//如果俩字符串同时找到\0,那么就相等
		{
			return 0;//返回0
		}
		value1_char++;//如果每一位相等,那么各自向后移动一位,直到找到\0,或不同
		value2_char++;
	}
	return *value1_char - *value2_char;//如果不同则返回目标位-源字符串位,依据结果判断大小
}

int main()
{
	char value1_char[] = "good";
	char value2_char[] = "good";
	int temp = my_strcmp(value1_char, value2_char);
	if (temp > 0)//判断
	{
		puts(">");
	}
	else if (temp < 0)
	{
		puts("<");
	}
	else
	{
		puts("==");
	}
	printf("%d", temp);
	return 0;
}

结果:


(4)strcmp函数的缺陷


(二)、strncmp函数·(有限制)字符串比较


(1)strncmp函数原型解析


(2)strncmp函数的使用


(3)strncmp函数的模拟实现

上代码:

//strncmp函数的模拟实现
int my_strncmp(const char* v_1, const char* v_2, int temp)
{
	assert(v_1 && v_2);//判断两个参数是否为空指针
	while ((--temp) && (*v_1 == *v_2))//如果temp为0 并且 *v_1 != *v_2时退出循环
	{
		if (*v_1 == '\0' && *v_2 == '\0')//如果*v_1 && *v_2同时等于/0则代表相等
		{
			return 0;//返回0
		}
		v_1++;//逐个向后
		v_2++;
	}
	return *v_1 - *v_2;//如果某元素不相等则直接返回,*v_1-*v_2
}

int main()
{
	char vate1_char[] = "abfdef";
	char vate2_char[] = "abceff";
	int temp = my_strncmp(vate1_char, vate2_char, 6);//测试1~6之间
	if (temp > 0)//判断部分
	{
		puts("vate1_char > vate2_char");
	}
	else if (temp < 0)
	{
		puts("vate1_char < vate2_char");
	}
	else
	{
		puts("vate1_char == vate2_char");
	}
	printf("%d", temp);
	return 0;
}

结果:










四、子串判断


(1)strstr函数原型解析


(2)strstr函数的使用

上代码:

//strstr库函数的使用
int main()
{
	char vate_1[] = "assefussacusr";
	char vate_2[] = "ssac";
	char* ret = strstr(vate_1, vate_2);
	if (NULL == ret)//判断是否为空指针
	{
		puts("找不到子集!");
	}
	else
	{
		puts("找到子集了!");
		printf("%s\n", ret);
	}
	return 0;
}

结果:


(3)strstr函数的模拟实现

上代码:

//strstr函数的模拟实现

char* my_strstr(const char* value1_char, const char* value2_char)
//查找源字符串是否为目标字符串中的子集,无需修改故加const确保源数据不被修改,保证数据安全性
{
	assert(value1_char && value2_char);//断言,确保指针不为空
	const char* value1 = value1_char;//创建指针保存目标字符串首元素地址,用于以下操作(添加const保证与目标字符串类型一致)
	const char* value2 = value2_char;//创建指针保存源字符串元素首元素地址,用于找不到时的回位操作
	const char* temp = value1_char;//保证越过不含可能性的元素地址
	while (*temp)
    //temp为'\0'时停止。
    //在此,以temp作为循环条件有两项原因:
    //1、起到每一位的遍历作用。在此期间会产生两个通道:
    //(1)、遍历目标元素(value)过程未找到源字符串(value)中的'/0',却找到目标元素中'\0',那么返回空指针,表示未找到value2包含在value1中的子集。
    //(2)、中途value2找到字符末尾'\0',那么返回value1中子集的起始位置。
    //2、筛选作用
	{
        value1 = temp;
		value2 = value2_char;
		while (*value1 && *value2 && (*value1 == *value2))
        //循环条件解析:
        //(1)、如果value1为'\0',那么退出第二层循环进入第一次循环,temp将会不断++,赋值给value1,直到temp找到'\0'时退出返回空指针。
        //(2)、如果value2(源字符串)为'\0',那么将退出第二层循环进入分支语句,返回temp在value1中所标记的子集的首元素地址,表示找到子集。
        //(3)、如果*value1 == *value2则表示二者元素目前相等,那么*value1与*value2共同推进,如果二者不同则退出进行调整环节。
		{
			value1++;
			value2++;
		}
		if (*value2 == '\0')
		{
			return (char*)temp;//强转原const修饰的字符指针常变量为字符指针,确保返回与接收类型的一致。
		}
		temp++;
        //temp发生++,则说明发生了value1 != value2的情况,那么temp此时将所记录的二者相等元素的地址进行推进,保证下一次的value1与value2对比时不是从原匹配位置再次进行比对。避免了死循环的发生,增加了找到子集的可能;
	return NULL;
}

int main()
{
	char value1_char[] = "vboogoosff";
	char value2_char[] = "oos";
	char* ret = my_strstr(value1_char, value2_char);//字符指针接收
	if (NULL == ret)//判断
	{
		puts("找不到子串");
	}
	else
	{
		printf("%s", ret);
	}
	return 0;
}


五、字符串分割操作


(1)strtok函数原型解析


(2)strtok函数的使用

上代码:

//strtok函数的使用
int main()
{
	char vate_1[] = "life/is.w onderful!";
	char vate_2[] = "/. ";
	char* ret = NULL;
	for (ret = strtok(vate_1, vate_2); ret != NULL; ret = strtok(NULL, vate_2))
	{
		printf("%s\n", ret);
	}
	return 0;
}

结果:



六、预警函数


(1)strerror函数


(2)perror函数

七、最后

举报

相关推荐

0 条评论