【C语言】字符函数和字符串函数

目录

一、字符分类函数

二、字符转换函数

三、strlen的使用和模拟实现

3.1 使用的注意事项

3.2 strlen的模拟实现

3.2.1 计数器方式

3.2.2 指针-指针方式

3.2.3 不能创建临时变量(使用递归)

3.3 size_t和unsigned int的区别 

四、strcpy的使用和模拟实现

4.1 使用的注意事项

 4.2 strcpy的模拟实现

五、strcat的使用和模拟实现

5.1 使用的注意事项

5.2 strcat的模拟实现

六、strcmp的使用和模拟实现

6.1 使用的注意事项

6.2 strcmp的模拟实现

七、strncpy的使用和模拟实现

7.1 使用的注意事项

7.2 strncpy的模拟实现

八、strncat的使用和模拟实现

8.1 使用的注意事项

8.2 strncat的模拟实现

九、strncmp的使用和模拟实现

9.1 使用的注意事项

9.2 strncmp的模拟实现

十、strstr的使用和模拟实现

10.1 使用的注意事项

10.2 strstr的模拟实现

十一、strtok函数的使用

十二、strerror函数的使用

12.1 使用举例

12.2 perror和strerror的区别

编程中我们经常遇到需要处理字符和字符串的情况,为了能够让我们更加方便地操作,C语言标准库提供了一系列相关的库函数 。

一、字符分类函数

C语⾔中有⼀系列的函数是专⻔做字符分类的,也就是⼀个字符是属于什么类型的字符的。

这些函数的使⽤都需要包含⼀个头⽂件是 ctype.h

这些函数的使用方法都十分类似,比如说:

int islower ( int c );

通过返回值来说明是否是⼩写字⺟,如果是⼩写字⺟就返回⾮0的整数,如果不是⼩写字⺟,则返回 0。(其他字符分类函数也是如此)

例题:写⼀个代码,将字符串中的小写字⺟转⼤写,其他字符不变。

int main()
{
	char str[100] = {0};
	gets(str);//从键盘输入一个字符串放进str数组中
	char c;
	int i = 0;
	while (str[i])
	{
		c = str[i];
		if (islower(c))
			c -= 32;//小写字母转大写字母
		putchar(c);//将字符打印出来
		i++;
	}
	return 0;
}

二、字符转换函数

C语⾔提供了2个字符转换函数:

int tolower ( int c ); //将参数传进去的⼤写字⺟转⼩写 
int toupper ( int c ); //将参数传进去的⼩写字⺟转⼤写

上⾯的代码,我们将⼩写转⼤写,是-32完成的效果,有了转换函数,就可以直接使⽤ tolower 函 数。

int main()
{
	char str[100] = { 0 };
	gets(str);//从键盘输入一个字符串放进str数组中
	char c;
	int i = 0;
	while (str[i])
	{
		c = str[i];
		if (islower(c))
			c =toupper(c);//小写字母转大写字母
		putchar(c);//将字符打印出来
		i++;
	}
	return 0;
}

三、strlen的使用和模拟实现

字符串相关函数的头文件string.h

strlen即string length(计算字符串的长度)

size_t strlen ( const char * str );

3.1 使用的注意事项

1、字符串以 '\0' 作为结束标志,strlen函数返回的是在字符串中 '\0' 前⾯出现的字符个数(不包含 '\0' )。

2、参数指向的字符串必须要以 '\0' 结束,如果没有,就可能会出现越界访问,会返回一个随机值。

int main()
{
	char str[] = { 'a','b','c' };
	strlen(str);
	printf("%zd", strlen(str));
}

运行结果:42 (属于随机值)

3、注意函数的返回值为size_t,是⽆符号的(占位符是%zd)

易错点:利用strlen的返回值相减

int main()
{
	char str1[] = "ab";
	char str2[] = "abc";
	if (strlen(str1) - strlen(str2) > 0)
		printf("大于\n");
	else 
		printf("小于\n");
}

输出结果:大于

 按照我们的理解,str1的长度是2,str2的长度是3,为什么结果会是大于?

就是因为strlen的返回值是无符号整形,虽然2-3=-1但是不加以转换的话,会被当成无符号数,所以对应的值也是正数,如果这里想这么用的话,就必须把strlen的返回值强制类型转化成int类型才能算。

int main()
{
	char str1[] = "ab";
	char str2[] = "abcdefg";
	if ((int)strlen(str1) -(int) strlen(str2) > 0)
		printf("大于\n");
	else 
		printf("小于\n");
}

3.2 strlen的模拟实现

3.2.1 计数器方式

size_t my_strlen1(const char* str)
{
	size_t count = 0;
	assert(str);//确保传的不是空指针
	while (*str)//遇到\0停止
	{
		count++;
		str++;
	}
	return count;
}

3.2.2 指针-指针方式

size_t my_strlen2(const char* str)
{
	assert(str);//确保传的不是空指针
	char* p = str;//用来遍历
	while (*p)
		p++;
	return p - str;//指针减指针得到中间的元素个数
}

3.2.3 不能创建临时变量(使用递归)

size_t my_strlen3(const char* str)
{
	assert(str);//确保传的不是空指针
	//比如“abcdef”的长度 可以拆成1+“bcdef” 再拆成1+1+“cdef”…………
	if (*str == '\0')
		return 0;
	else
		return 1 + my_strlen3(str + 1);
}

3.3 size_t和unsigned int的区别 

1、size_t大小不固定,取决于操作系统(在32位环境中,是4个字节,在64位环境中是8个字节)。unsigned int通常4个字节

2、“size_t”用于存储对象的大小,而“unsigne int”用于存储整数值

四、strcpy的使用和模拟实现


strcpy即string copy(字符串的拷贝)

char* strcpy(char * destination, const char * source );

 destination是目标字符串,source是源字符串,strcpy的作用是将源字符串拷贝到目标字符串,并返回指向新的目标字符串的指针

4.1 使用的注意事项

1、 源字符串必须以 '\0' 结束。

2、会将源字符串中的 '\0' 拷⻉到⽬标空间。

3、⽬标空间必须⾜够⼤,以确保能存放源字符串。

4、⽬标空间必须可修改。

4.2 strcpy的模拟实现

 4.2 strcpy的模拟实现

char* my_strcpy(char* des, const char* src)
{
	char* ret = des;//记录返回值
	assert(des && src);//确保没有传NULL指针
	while (*des++ = *src++)
//++优先于*,但是后置加加是先使用完再++,所以实际上还是相当于src解引用的元素,知道src解引用出\0,循环正好停止
		;//空语句
	return ret;
}

五、strcat的使用和模拟实现

strcat即string concatenate(字符串连接)

char * strcat ( char * destination, const char * source );

destination是目标字符串,source是源字符串,strcat的作用是将源字符串连接到目标字符串后面,并返回指向新的目标字符串的指针。

5.1 使用的注意事项

1、源字符串必须以 '\0' 结束。

2、⽬标字符串中也得有 \0 ,否则没办法知道追加从哪⾥开始。

3、⽬标空间必须有⾜够的⼤,能容纳下源字符串的内容。

4、⽬标空间必须可修改。

5、不能自己给自己追加!(易错),会造成死循环,没有尽头

      因为strcat的实现方法是将源字符串从目标字符串的\0开始拷贝(会覆盖\0),如果两个参数都是传同一个字符串,就会造成永远找不到\0,造成死循环

5.2 strcat的模拟实现

char* my_strcat(char* des, const char* src)
{
	char* ret = des;//记录返回值
	assert(des && src);//确保没有传NULL指针
	//因为是从目标字符串的\0开始连接的,所以要先找到目标字符串的/0
	//然后再逐个拷贝源字符串的内容
	while (*des)
		des++;
	while (*des++ = *src++)
		;
	return ret;
}

 

六、strcmp的使用和模拟实现

strcmp即string compare(字符串比较)

int strcmp ( const char * str1, const char * str2 );

6.1 使用的注意事项

1、第⼀个字符串⼤于第⼆个字符串,则返回⼤于0的数字 ◦

2、第⼀个字符串等于第⼆个字符串,则返回0 

3、第⼀个字符串⼩于第⼆个字符串,则返回⼩于0的数字

4、比较字符串的方法:⽐较两个字符串中对应位置上字符ASCII码值的⼤⼩,小的字符所在的字符串小于另一个字符串。

6.2 strcmp的模拟实现

int my_strcmp(const char* str1, const char* str2)
{
	assert(str1 && str2);//确保没有传NULL指针
	while (*str1 == *str2&&str1!='\0')
//当两个指向的字符相等时或者str1和str2还没等于\0的时候
	{
		//迭代
		str1++;
		str2++;
	}
	//当两个指向的字符不相等时
	return *str1 - *str2;//大于0返回正数,小于0返回负数
}

 

七、strncpy的使用和模拟实现

char * strncpy ( char * destination, const char * source, size_t num );

相比较于strcpy,可以拷贝源字符串中指定个数的字符 

7.1 使用的注意事项

1、拷⻉num个字符从源字符串到⽬标空间。

2、如果源字符串的⻓度⼩于num,则拷⻉完源字符串之后,在⽬标的后边追加0,直到num个。

7.2 strncpy的模拟实现

char* my_strncpy(char* des, const char* src, size_t num)
{
	char* ret = des;//记录返回值
	assert(des && src);//确保没有传NULL指针
	while (num)
	{
		if (src)//如果src不是\0,则继续拷贝
			*des = *src;
		else//如果src已经走到\0了,循环还没结束,就追加/0,直到num个
			*des = '\0';
		//迭代
		des++;
		if(src)//如果已经到\0了,不能再++了,否则越界
		src++;
		num--;
	}
	return ret;
}

 

八、strncat的使用和模拟实现

 char * strncat ( char * destination, const char * source, size_t num );

 想比较于strcat,可以追加源字符串的指定个数的字符

8.1 使用的注意事项

1、将source指向字符串的前num个字符追加到destination指向的字符串末尾,再追加⼀个 \0 字符

2、如果source 指向的字符串的⻓度⼩于num的时候,只会将字符串中到 \0 的内容追加destination指向的字符串末尾

8.2 strncat的模拟实现

char* my_strncat(char* des, const char* src, size_t num)
{
	char* ret = des;//记录返回值
	assert(des && src);//确保没有传NULL指针
	//des要先走到\0的位置
	while (*des)
	{
		des++;
	}
	//从des的\0出开始追加src
	while (num&&src!='\0')//src会先走到\0,此时就跳出循环
	{
		//拷贝
		*des = *src;
		//迭代
		des++;
		src++;
		num--;
	}
	//num个元素拷贝完成,则给结尾补上\0
	*des = '\0';
	return ret;
}

 

九、strncmp的使用和模拟实现

int strncmp ( const char * str1, const char * str2, size_t num );

相比较于strcmp,可以指定最多比较num个字母

9.1 使用的注意事项

⽐较str1和str2的前num个字符,如果相等就继续往后⽐较,最多⽐较num个字⺟,如果提前发现不⼀ 样,就提前结束,⼤的字符所在的字符串⼤于另外⼀个。如果num个字符都相等,就是相等返回0.

9.2 strncmp的模拟实现

int my_strncmp(const char* str1, const char* str2, size_t num)
{
	assert(str1 && str2);//确保没有传NULL指针
	if(num==0)//如果传的是0,那么结果就是返回0
		return 0;
	while (--num&&*str1==*str2&&*str1!='\0')
		//当num变成0的时候或者两个指向的字符相等时或者str1和str2还没等于\0的时候 
	{
		//迭代
		str1++;
		str2++;
	}
	return *str1-*str2;
}

 

十、strstr的使用和模拟实现

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

10.1 使用的注意事项

1、函数返回字符串str2在字符串str1中第⼀次出现的位置

2、字符串的⽐较匹配不包含 \0 字符,以 \0 作为结束标志

10.2 strstr的模拟实现

char* my_strstr(const char* str1, const char* str2)
{
	assert(str1 && str2);//确保不传NULL进来
	char* cur = str1;//cur用来记录遍历点
	char* s1 = NULL, * s2 = NULL;//s1用来遍历字符串1,s2用来遍历字符串2
	if (*str2 == '\0')//库的strstr函数规定如果要找的是空字符串,那么返回str1
	{
		return (char*)str1;//返回值是char*,要强转
	}
	while (*cur)
	{
		s1 = cur;
		s2 = str2;
		while (*s1 == *s2&&*s1&&*s2)//当循环第一次找到第一个字符是相等的,开始确认是不是我们要找的字符串
			//找到的情况:
			// 1、*s1和*s2都是\0,
			// 2、*s2是\0
			// 没找到的情况:
			// 1、*s1!=*s2
			// 2、*s1是\0,但是*s2不是\0
		{
			s1++;
			s2++;
		}
		//跳出循环后,判断s2是否走到\0
		if (*s2 == '\0')
			return cur;
		//迭代
		cur++;
	}
	return NULL;
}

 

十一、strtok函数的使用

char * strtok ( char * str, const char * sep);

1、sep参数指向⼀个字符串,定义了⽤作分隔符的字符集合

2、第⼀个参数指定⼀个字符串,它包含了0个或者多个由sep字符串中⼀个或者多个分隔符分割的标记。

3、strtok函数找到str中的下⼀个标记,并将其⽤ \0 结尾,返回⼀个指向这个标记的指针。(注: strtok函数会改变被操作的字符串,所以在使⽤strtok函数切分的字符串⼀般都是临时拷⻉的内容 并且可修改。)

4、strtok函数的第⼀个参数不为 NULL ,函数将找到str中第⼀个标记,strtok函数将保存它在字符串中的位置。

5、strtok函数的第⼀个参数为 NULL ,函数将在同⼀个字符串中被保存的位置开始,查找下⼀个标 记。

6、如果字符串中不存在更多的标记,则返回 NULL 指针。

int main()
{
	char arr[] = "jiudianjiu@163.com";
	//使用strtok会改变需分割字符串的内容,所以最好用拷贝的数据分割
	char *str = NULL;
	char* sep = "@.";
	for (str = strtok(arr, sep); str != NULL; str = strtok(NULL, sep))
//for循环的书写恰好满足strtok,因为只有第一次(初始化)传str,
//然后就一直传NULL(累加条件),直到str变成NULL(结束条件)
		printf("%s\n", str);
	return 0;
}

这里我们可以发现c语言库里的strtok函数里面应该有个静态变量, 他调用完后并不会被销毁,所以我们后面传NULL过去的时候他仍然能记住保存的位置在哪里

 

十二、strerror函数的使用

 char * strerror ( int errnum );

strerror函数可以把参数部分错误码对应的错误信息的字符串地址返回来。

      在不同的系统和C语⾔标准库的实现中都规定了⼀些错误码,⼀般是放在 errno.h 这个头⽂件中说明的,C语⾔程序启动的时候就会使⽤⼀个全面的变量errno来记录程序的当前错误码,只不过程序启动 的时候errno是0,表⽰没有错误,当我们在使⽤标准库中的函数的时候发⽣了某种错误,就会讲对应的错误码,存放在errno中,⽽⼀个错误码的数字是整数很难理解是什么意思,所以每⼀个错误码都是有对应的错误信息的。strerror函数就可以将错误对应的错误信息字符串的地址返回。

12.1 使用举例

int main ()
{
 FILE * pFile;
 pFile = fopen ("unexist.ent","r");
 if (pFile == NULL)
 printf ("Error opening file unexist.ent: %s\n", strerror(errno));
 return 0;
}

输出结果:Error opening file unexist.ent: No such file or directory 

12.2 perror和strerror的区别

perror函数是打印完参数部分的字符串后,再打印一个冒号和一个空格,然后再打印错误信息。

int main ()
{
 FILE * pFile;
 pFile = fopen ("unexist.ent","r");
 if (pFile == NULL)
 perror("Error opening file unexist.ent");
 return 0;
}

输出结果:Error opening file unexist.ent: No such file or directory 

使用场景的不同:

perror函数不需要error.h的头文件就可以直接打印储存在erron这个全局变量的错误信息,相对比较好用,但是也比较粗暴,会直接打印在屏幕上。

strerror函数需要使用error.h的头文件,因为传参传的是erron这个变量,他比较不粗暴,是暂时将该错误信息存储起来,我们可以把它写进文件里,也可以先记录但是暂时不打印,比较灵活。

 

评论 92
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值