脆皮之“字符函数与字符串函数”宝典

hello,大家好呀,感觉我之前有偷偷摸鱼了,今天又开始学习啦。加油!!!
在这里插入图片描述

1. 字符分类函数

C语言中有一系列的函数是专门做字符分类的,且这些函数在使用时都需要包含头文件:ctype.h

在这里插入图片描述

这些函数的使用大同小异,我举其中的一个例子:

从上图可以看出,函数islower是用来判断是否为小写字母,我们可以在cplusplus搜索islower函数,它给出的是:如果是小写字母,则返回非0值;如果不是小写字母,则返回0.

int main()
{
	int c = islower('A');
	int q = islower('a');
	printf("c=%d\n", c);
	printf("q=%d\n", q);
	return 0;
	//打印结果是:c=0;q=2
}

练习写一个代码:将字符串中的小写字母转大写,其他字符不变。(在学习字符转换函数之后有另外一种方式,大家可以尝试一下)

#include<stdio.h>
#include<ctype.h>
int main()
{
	char arr[] = "I am very sad." ;
	int i = 0;
	while (arr[i] != '\0')
	{
		if (islower(arr[i]))
		{
			arr[i] = arr[i] - 32;
		}
		i++;
	}
	//再将字符串输出,看是否变成大写
	printf("%s\n", arr);
	return 0;
}

2. 字符转换函数

C语言提供了2个字符转换函数:
int tolower ( int c ); ------- 转成小写--------- //将参数传进去的大写字母转小写
int toupper ( int c );------- 转成大写----------//将参数传进去的小写字母转大写
int c的意思是传入一个字符(字符的本质就是ascll码值),int tolower的意思是返回值为整数

int main()
{
	char ch = tolower('A');
	printf("%c\n", ch);

	char hh = toupper('a');
	printf("%c\n", hh);
}

之前小写转大写可以将if语句里面改掉

int main()
{
	char arr[] = "I am very sad." ;
	int i = 0;
	while (arr[i] != '\0')
	{
		if (islower(arr[i]))
		{
			arr[i] = toupper(arr[i]);
		}
		i++;
	}
	//再将字符串输出,看是否变成大写
	printf("%s\n", arr);
	return 0;
}

3. strlen的使用和模拟实现

3.1 strlen 的使用

1.使用strlen需要包含头文件:string.h
2.字符串以’\0’ 作为结束标志。

  • 比如:“abc” 其实就是‘a’ ‘b’ ‘c’ ‘\0’,一共4个字符。但是strlen只统计’\0‘之前的字符个数。(不包含’\0’)
  • 如果字符数组中是char arr[]={‘a’,‘b’,‘c’};的话,则是没有\0的,什么时候遇到\0不确定。
int main()
{
	char arr[] = "aaattt";
	size_t len = strlen(arr);
	printf("%d\n", len);//6
	char qqq[]={'a','b'};
	size_t lee = strlen(qqq);
	printf("%d\n",lee);//随机值
	return 0;
}

3.strlen的返回值是size_t(即无符号整数)

int main()
{
	char ch1[] = "abc";
	char ch2[] = "abcdef";
	if (strlen(ch1) - strlen(ch2) < 0)
		//3         -      6
	{
		printf("ch1<ch2");
	}
	else
	{
		printf("ch1>ch2");
	}
}
//最终的结果是ch1>ch2

3-6=-3,小于0,为什么输出是大于?

在这里说明,strlen的返回值是size_t,无符号整数-无符号整数=还是无符号整数,所以strlen(ch1) - strlen(ch1)的结果其实>0,所以输出>。

如果非要比较,可以将strlen的返回值强制类型转换为int,(int)strlen(ch1)-(int)strlen(ch2)。或者直接比较: if (strlen(ch1) < strlen(ch2) )

3.1 strlen 的模拟

1.计算器方法

(1.) 这是最初的版本,之后优化。

size_t my_strlen(char* str)
{
	int count = 0;
	while (*str != '\0')
	{
		count++;//个数+1
		str++;//下一个元素的地址
	}
	return count;
}
int main()
{
	char ch[] = "aaaqqq";
	size_t len = my_strlen(ch);//传数组传的是数组名(这里代表的是数组首元素地址)
	printf("%d\n", len);
}

(2.)1.万一指针是空指针呢?我们先用断言判断一下
2.我们只是想遍历一遍数组的每个元素,找到\0,并不希望有人将元素修改了,所以用const修饰*ch

#include<assert.h>
size_t my_strlen(const char* str)//用指针变量来接收地址
{
	
	int count = 0;
	assert(str != NULL);
	while (*str != '\0')
	{
		count++;//个数+1
		str++;//下一个元素的地址
	}
	return count;
}
int main()
{
	char ch[] = "aaaqqq";
	size_t len = my_strlen(ch);//传数组传的是数组名(这里代表的是数组首元素地址)
	printf("%d\n", len);
}

2.指针-指针的方法

用起始地址-\0的地址=就是中间的元素个数

这几种方法int main函数里的内容都一样,之后就不再展示,只展示my_strlen函数里的内容

size_t my_strlen(const char* str)
{
	int count = 0;
	assert(str != '\0');
	int start = str;//这里str还是起始元素的地址
	while (*str != '\0')
	{
		//通过这个循环,使得str里面放的是\0的地址
		str++;
	}
	int end = str;
	return end - start;
}

3.递归的方法

在这里插入图片描述

size_t my_strlen(const char* str)
{
	if (*str != '\0')
		return 1 + my_strlen(str + 1);
	else
		return 0;
}

长度不受限制的字符串函数strcpy,stracat,strcmp

4. strcpy的使用和模拟实现

4.1 strcpy的使用

在这里插入图片描述
解析:从source那里复制字符串,给目的地。

注意内容:

  1. 源字符串必须以’\0’ 结束。(没有’\0’,strcpy无法结束)
int main()
{
	char arr1[] = "aaajjjkkkiii";
	char arr2[90] = { 0 };
	strcpy(  arr2   ,       arr1);
	     //接收内容 ,    被复制的
	  //arr2数组首元素地址,arr1首元素地址
	printf("%s\n", arr2);
}
  1. 会将源字符串中的’\0’ 拷贝到目标空间。

从下图可知:\0是被复制的

在这里插入图片描述

  1. ⽬标空间必须足够大,以确保能存放源字符串。
  1. 目标空间必须可修改。
char* p = "xxxxxxxxxxxx";//这个是无法修改的

4.2 strcpy的模拟实现

void my_strcpy(char* dest, char* src)
{
   //assert(dest != NULL);
   //assert(src != NULL);
    assert(dest && src);
	while (*src != '\0')
	{
		*dest = *src;
		dest++;
		src++;
	}
	*dest = *src;//这里是为了'\0'
}
int main()
{
	char arr1[] = "aaajjjkkklllhhh";
	char arr2[90] = { 0 };
	my_strcpy(arr2, arr1);
	printf("%s\n", arr2);
}
  1. 即我们自己写一个函数,使其可以实现strcpy函数的功能。在形式上,要与strcpy函数的形式保持一致。
my_strcpy(arr1,arr2);
  1. 看我上图的绿色粗框框,为什么返回值是char*类型?
    在这里插入图片描述

为的是实现链式访问,strcpy函数返回的是目标空间(目的地)的起始地址(将源头的内容拷贝到目的地之后,将目的地的起始地址返回回来。)

char* my_strcpy(char* dest, char* src)
{
	assert(dest && src);

	char* ret = dest;
	while (*src != '\0')
	{
		*dest = *src;
		dest++;
		src++;
	}
	*dest = *src;
	return ret;
}

那有没有一种方法将内容和\0一起复制,不用分开呢
后置++的级别是之后再执行,在*dest++中,先解引用旧的dest,再++。

*dest++ = *src++//这是先*src,复制给*dest,然后再src++,dest++..........
//这和 //*dest = *src;
	   //dest++;
       //src++;一样
char* my_strcpy(char* dest, char* src)  //这里的src意思是source
{
	assert(dest && src);

	char* ret = dest;
	//拷贝过去字符串后,判断表达式的值。最后一次是\0,已经复制过去了,发现是\0,判断为假,不再执行++,停止循环
	while (*dest++ = *src++)
	{
		;
	}
	return ret;
}
int main()
{
	char arr1[] = "aaajjjkkklllhhh";
	char arr2[90] = { 0 };
	my_strcpy(arr2, arr1);
	printf("%s\n", arr2);
}

5. strcat的使用和模拟实现

5.1 strcat的使用(头文件:string.h)

在这里插入图片描述

简单来说,就是将源字符串(source)接到目的地字符串(d )后边(将\0覆盖(因为在\0之后有东西,我们也是看不到的),在接完之后,再加上\0)

  1. 先找到目标空间的末尾\0(要求目标空间有\0)
  2. 再将源字符串拷贝过来(但是结尾也得是\0,所以源字符串末尾也必须有\0)
  3. 保证目标空间必须足够大,且可以修改。
int main()
{
	char arr1[90] = "nihao";
	char arr2[90] = "shijie";
	strcat(arr1, arr2);
//arr1变长的目标空间,arr2是被接的部分
	printf("%s\n", arr1);
}

5.2 strcat的模拟实现

不要自己给自己追加昂.

  1. 自定义函数形式与我们所查询的保持一致
  2. 遍历目标空间,找到\0,从第一个元素开始,如果不是\0就dest++,知道*dest等于\0
  3. 拷贝,从\0开始

在这里插入图片描述

char* my_strcat(char* dest, char* src)
{
	while (*dest != '\0')
		dest++;
	//找到\0了,接下来拷贝
	while (*dest++ = *src++)
		;
	//之后再返回目标空间的起始地址
	return dest;
}
int main()
{
	char arr1[90] = "nihao";
	char arr2[90] = "shijie";
	my_strcat(arr1, arr2);
	printf("%s\n", arr1);
}

6. strcmp的使用和模拟实现

6.1 strcmp的使用

strcpm是用来来比较字符串的,是比较对应位置的字符的大小,并不是看字符串长度。

在这里插入图片描述

返回值:如果左边字符串>右边,返回值>0。左边字符串=右边,返回值=0。左边字符串<右边,返回值<0。

int main()
{
	char arr1[] = "aaajjjkkkl";
	char arr2[] = "jinifoshljnflweij";
	int c=strcmp(arr1, arr2);
	printf("%d\n", c);
}

6.2 strcmp的模拟实现

在模拟实现时,需要注意的点

  1. 我们只是希望通过指针变量找到开始的变量,比较它们,并不希望它们被改变,所以用const修饰。
  2. 如果指向的两个元素它们相等,则不用做什么改变,只需要++,到下一个元素的地址。
  3. 当不相等时,则需要比较
  4. 如果两个数组的所有元素都相等,则返回0(在那个循环比较里)
  5. 保险起见,可以用assert来判断是否为空指针。
int my_strcmp(const char* str1,const char* str2)
{
	assert(str1 && str2);
	while (*str1 == *str2)
	{
		if (*str1 == '\0')
			return 0;
		str1++;
		str2++;
	}
	if (*str1 > *str2)
		return 1;
	else
		return -1;
}
int main()
{
	char arr1[] = "aaajjjkkkl";
	char arr2[] = "jinifoshljnflweij";
	int c = my_strcmp(arr1, arr2);//arr1,arr2代表的都是首元素地址,用指针变量接收地址
	printf("%d\n", c);
}

长度受限制的字符串函数strncpy,strncat,strncmp

7. strncpy函数的使用

  • 让拷贝几个就是几个,不会有\0。

strncpy可控制复制的字符串的长度,而不是像strcpy一样全部复制。
在这里插入图片描述
num是从source那里可拷贝的最大字符个数。

在这里插入图片描述

在这里插入图片描述

8. strncat函数的使用

  • 假设让追加3个,在追加完之后,还有附带一个\0。
    在这里插入图片描述

在这里插入图片描述

num是用来控制追加的字符个数。
在这里插入图片描述

在这里插入图片描述

9. strncmp函数的使用

int main()
{
	char arr1[90] = "abcdef";
	char arr2[90] = "abcq";
	int ret = strncmp(arr1, arr2, 3);
	      //意思是只比较两个字符串前三个字符
	printf("%d\n", ret);
	return 0;
}

10. strstr的使用和模拟实现

10.1 strstr的使用

  1. 在str1中寻找str2字符串第一次出现的位置,如果找到了就返回str2在str1中第一次出现的起始地址。如果找不到,就返回空指针。
  2. 注意,并不是第一个字符一样就会返回第一次出现的地址,需要都一样。
    比如arr1:abcdef,arr2是cq。它们两个只有c是一样的,c之后的并不一样。该返回值是空指针,null。
int main()
{
	char arr1[] = "asdfhkl";
	char arr2[] = "df";
	char* ret=strstr(arr1, arr2);
	//为什么用char*接收,因为这个函数返回的是地址
	//arr2在arr1中第一次出现时d的地址,打印则从d开始往后打印
	printf("%s\n", ret);

	//最终的打印结果是dfhkl
}

10.2 strstr的模拟实现

  1. 刚开始,一直在找*str1中与str2的首元素相等的元素。当我们在str1中找到第一个与str2首元素相同的元素,将那个位置记录下来。然后再往后走,看之后的几个是否一样,若在str2找到\0时,那几个都一样,√

在这里插入图片描述

  1. 有可能存在多次匹配的情况。
    比如arr1:abbder。arr2:bde。如果是arr1的第一个b,则后面就不对了。如果匹配的是arr1的第二个b,则后面的de就配上了。

  2. 有可能遇见arr1较短的情况。
    比如arr1:abcdefg。arr2:fgfg。(返回空指针)

  3. 还有一种特殊情况:如果arr2是空字符串呢?这时会直接返回arr1。

#include<assert.h>
         //arr11是用来接收数组arr1的首元素地址,arr22同理
const char* my_strstr(const char* arr11,const char* arr22)
{
	assert(arr11 && arr22);
	//创建三个指针变量s1,s2,cur(cur用来记录遇到第一个相同元素的地址,他刚开始是在arr1的首元素地址那)
	const char* s1 = NULL; //为什么用const,当我们把受限制(const)的arr11交给不受限制的s1,编译器会报警告
	const char* s2 = NULL;
	const char* cur = arr11;

	if (*arr22 == '\0')//特殊情况,arr22指向的是空字符串
		return arr22;
	//循环判断
	while (cur != '\0')  //其实这里也可以写成while(cur),当cur=0时,无法进入循环
	{
		s1 = cur;
		s2 = arr22; //每次重新匹配,s2都是arr22的第一个元素的地址
		while ((s1 != '\0') && (s2 != '\0') && (*s1 == *s2))//如何能进入真正的判断,有三个条件
		{                                                   //还可以写成while(s1&&s2&&(*s1==*s2))
			s1++;
			s2++;
		}

		//如果退出循环是因为,s1和s2一直相等,直到s2遇到\0了,那么这时就可以输出刚开始元素一样的起始位置:cur
		if (*s2 == '\0')
			return cur;

		//如果不满足循环条件(比如在之后*s2!=*s2)给退出了,接下来做什么?给cur++,然后给s1继续。
		cur++;
	}
	return NULL;
}
int main()
{
	char arr1[] = "asddfhkl";
	char arr2[90] = "s";
	char* ret = my_strstr(arr1, arr2);  //大家别忘了arr1代表的是数组arr1的首元素的地址
	//根据我们自定义的函数来返回内容,是否找到相关内容
	if (ret == NULL)
		printf("未在arr1里找到与arr2相同的内容\n");
	if (ret != NULL)
		printf("%s\n", ret);
}
#include<assert.h>
const char* my_strstr(const char* arr11, const char* arr22)
{
	assert(arr11 && arr22);
	
	const char* s1 = NULL; 
	const char* s2 = NULL;
	const char* cur = arr11;
	if (*arr22 == '\0')
		return arr22;
	while (cur)  
	{
		s1 = cur;
		s2 = arr22; 
		while (s1  && s2 && (*s1 == *s2))
		{                                                   
			s1++;
			s2++;
		}
		if (*s2 == '\0')
			return cur;
		cur++;
	}
	return NULL;
}
int main()
{
	char arr1[] = "asddfhkl";
	char arr2[90] = "s";
	char* ret = my_strstr(arr1, arr2);  
	
	if (ret == NULL)
		printf("未在arr1里找到与arr2相同的内容\n");
	if (ret != NULL)
		printf("%s\n", ret);
}

11. strtok函数的使用

  1. 开头解析

在这里插入图片描述

  1. 注意事项

在这里插入图片描述

int main()
{
	char arr1[] = "houppuur@qq.com";
	char sep[] = "@.";   //存放分隔符

	//arr1是肯定被修改的,我们可以将内容复制到另一个字符数组
	char str[90] = { 0 };
	strcpy(str, arr1);

	char* ret=strtok(str, sep);
	printf("%s\n", ret);
	//strtok函数的第⼀个参数不为NULL,函数将找到str中第⼀个标记,strtok函数将保存它在字符串中的位置
	//即先找到了@ 将它变为了\0,再返回⼀个指向[这个标记]的指针(指针用指针变量来接收,char*)

	ret = strtok(NULL, sep);
	//此时strtok函数的第⼀个参数为NULL,函数将在同⼀个字符串中被保存的位置开始,查找下⼀个标记
	printf("%s\n", ret);

	ret = strtok(NULL, sep);
	printf("%s\n", ret);
}

在这里插入图片描述

大家看着前面的代码试想一下,第一次用的buf,之后是NULL,像不像for循环的只需要第一次初始化,之后不用一样。(初始化;判断;调整部分)

此处的初始化是:ret=strtok(str, sep)
判断是:ret!=NULL
调整部分是:ret = strtok(NULL, sep)

	char arr1[] = "houppuur@qq.com";
	char sep[] = "@.";

	//arr1是肯定被修改的,我们可以将内容复制到另一个字符数组
	char str[90] = { 0 };
	strcpy(str, arr1);

	char* ret = NULL;
        //初始化                 //判断        //调整
	for (ret = strtok(str, sep); ret != NULL; ret = strtok(NULL, sep))
	{
		printf("%s\n", ret);
	}

12. strerror函数的使用

  1. strerror的头文件:#include<stdio.h>
  2. strerror将错误码对应的错误信息的字符串的起始地址返回(需要自己去printf)
  3. perror是将errno中错误的对应信息打印出来(先打印str指向的字符串,再打印冒号:和空格 ,再打印错误码对应的错误信息)【使用errno需要包含头文件:#include<stdio.h>
  4. perror==printf+strerror
#include<errno.h>
int main()
{
	FILE* pf = fopen("data.text", "r");//"r"是以只读的形式打开文件,如果文件不存在,则打开失败
	//打开失败之后会传给指针变量pf空指针
	
	if (pf == NULL)
	{
		printf("打开文件失败,原因是: %s\n", strerror(errno));//一旦调用库函数失败,会将错误码放在errno
		perror("打开文件失败,原因是");
		//大家运行出来后对比一下,会发现两者打印出来的一样,perror确实会自己打印:和空格
		return 1;
	}
	else
	{
		printf("打开文件成功\n");
		//如果想关闭文件
		fclose(pf);
		pf = NULL;
	}
	return 0;
}

打印结果:
在这里插入图片描述

  • 82
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值