开始前言
介绍完啦!那么接下来我们就......
发车!
目录
寄语
此篇博客是对字符串操作知识点的一个总结。字符串是一个特殊的存在,因为它不属于任何数据类型,因此,很多人在初次接触字符串相关操作的题目时,都不由得会无从下手。
为了确保知识点的完整性和具有实践性,我将会对每个知识点进行图演,与特殊情况解析,以便小伙伴们理解!
一、字符串拷贝操作
在各大笔试题目中,我们总能看到字符串的身影,而字符串的拷贝操作,则是考察我们对字符串特性熟悉与否的一个基本考点。
在第一节中,我们的目标是要弄清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;
}
结果:
情境二:
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;
}
结果:
情境一分析:
情境二分析:
(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;
}
结果:
(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;
}
结果:
情境二:
//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;
}
结果:
情境三:
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;
}
结果:
情境一分析:
vate1_char数组内部元素摆放:
情境二分析:
vate1_char数组内部元素摆放:
情境三分析:
(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;
}
结果:
二、字符串追加操作
(一)、strcat函数·(无限制)字符串追加
图演:
(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;
}
结果:
上代码:
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;
}
结果:
情境二:
上代码:
int main()
{
char vate_1[20] = "happy";
char vate_2[] = "life";
char* ret = strcat(vate_1, vate_1);
printf("%s\n", ret);
return 0;
}
结果:
情境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;
}
结果: