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


1.字符串函数

1.1求字符串长度

1.1.1strlen

在这里插入图片描述
例子

#include<stdio.h>
#include<string.h>
int main()
{
	char arr[20] = "abcdef";
	int len = strlen(arr);
	printf("%d", len);
	return 0;
}

答案
6

arr有7个字符,包括最后的‘\0’,strlen求出arr的长度是6,实际上求的是‘\0’之前的字符个数。
疑惑
为什么strlen的返回类型是size_t?
strlen求字符串长度,长度不可能是负数,所以返回类型设置为size_t(unsigned int)。

注意

int main()
{
	if (strlen("abc") - strlen("abcdef") > 0)
	{
		printf(">");
	}
	else
	{
		printf("<");
	}
}

答案
“>”

为什么?两个无符号类型相减得到的数类型还是无符号类型,所以得到的值不是负数而是一个非常大的数字。

1.1.2strlen的模拟实现

#include<assert.h>
int my_strlen(char* arr)
{
	assert(arr);//断言,一旦arr是NULL就会报错
	char* tmp = arr;
	while (*arr)
	{
		arr++;
	}
	return arr - tmp;//相同类型的指针相减返回之间的元素个数
}
int main()
{
	char arr[20] = "abcdef";
	int len = my_strlen(arr);
	printf("%d", len);
	return 0;
}

答案
6

1.2长度不受限制的字符串函数

1.2.1strcpy

在这里插入图片描述
例子

int main()
{
	char arr1[20] = { 0 };
	char arr2[] = "abcdef";
	strcpy(arr1, arr2);
	printf("%s", arr1);
	return 0;
}

注意
(1)目标数组必须足够大,能够容下被拷贝数组
(2)拷贝时遇到被拷贝数组的’\0’便结束拷贝,所以被拷贝数组必须包含’\0’
(3)拷贝时会将源数组(被拷贝数组)的’\0’一并拷贝过去
(4)目标数组必须可改变,不能用const修饰

1.2.2strcpy的模拟实现

char* my_strcpy(char*dest,const char*sour)//sour指向的内容无需改变,可以用const修饰
{
	assert(dest && sour);
	char* ret = dest;
	while (*dest++ = *sour++)//后置++先使用指针,解引用,再使指针指向下一个元素
	{
		;
	}
	return ret;
}
int main()
{
	char arr1[20] = { 0 };
	char arr2[] = "abcdef";
	my_strcpy(arr1, arr2);
	printf("%s", arr1);
	return 0;
}

答案
abcdef

1.2.3strcat

在这里插入图片描述
例子

int main()
{
	char arr1[20] = "hello";
	char arr2[] = " world";
	strcat(arr1, arr2);
	printf("%s", arr1);
	return 0;
}

答案
hello world

注意
(1)目标数组必须足够大,能够容下追加源数组后的字符串
(2)目标数组和源数组必须包括’\0’
(3)追加字符串时会在目标数组的’\0’处开始(包括’\0’),追加到源数组的’\0’为止(包括’\0’)
疑惑
数组自己追加自己会发生什么?
源头的’\0’被干掉,没有’\0’,就会死循环。所以自己给自己追加不要用strcat。

1.2.4strcat的模拟实现

char* my_strcat(char* dest, const char* sour)
{
	assert(dest && sour);
	char* tmp = dest;
	//首先找到目标数组的结束标志
	while (*dest)
	{
		dest++;
	}
	//接着开始追加
	while (*dest++ = *sour++)
	{
		;
	}
}
int main()
{
	char arr1[20] = "hello";
	char arr2[] = " world";
	my_strcat(arr1, arr2);
	printf("%s", arr1);
	return 0;
}

答案
hello world

1.2.5strcmp

在这里插入图片描述
例子

int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "abqwer";
	int ret = strcmp(arr1, arr2);
	if (ret > 0)
	{
		printf("arr1>arr2\n");
	}
	else if (ret = 0)
	{
		printf("arr1==arr2\n");
	}
	else
	{
		printf("arr1<arr2\n");
	}
}

答案
arr1<arr2

注意
比较的时候比较对应字符的ASCII码值,如果相等则同时指针向下一个字符,再次比较其ASCII码值,不断循环直到找到对应字符不相等的情况,当str1对应字符>str2对应字符,则返回大于0的数字,当str1对应字符<str2对应字符,则返回小于0的数字,若相等则返回0。

1.2.6strcmp的模拟实现

int my_strcmp(const char* str1, const char* str2)
{
	while (*str1 == *str2 && *str1!='0'&&*str2!='\0')
	{                       当两个字符串相同时,防止其死循环所以要保障str1和str2都不等与'\0'
		str1++;
		str2++;
	}
	return *str1 - *str2;//无论循环结束还是跳出循环,都可达到想要的效果
}
int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "abqwer";
	int ret = my_strcmp(arr1, arr2);
	if (ret > 0)
	{
		printf("arr1>arr2\n");
	}
	else if (ret = 0)
	{
		printf("arr1==arr2\n");
	}
	else
	{
		printf("arr1<arr2\n");
	}
}

答案
arr1<arr2

1.3长度受限制的字符串函数

以上三个函数都是长度不受限制的字符串函数,直到’\0’为止。但也让人觉得这些函数不安全,因为可能目标空间不够大,在拷贝和追加时又是到源数组的’\0’为止,可能导致越界。那么有什么解决方法吗?

1.3.1strncpy

在这里插入图片描述

例子

#include<string.h>
int main()
{
	char arr1[20] = { 0 };
	strncpy(arr1, "abcdef", 3);
	printf("%s", arr1);
	return 0;
}

答案
abc

注意
strncpy是将源数组的num个字符拷贝到目标数组,如果num>源数组的字符时,将源数组的所有字符拷贝过去,再在目标数组后面添加’\0’,直到满足拷贝num个字符为止;如果num<源数组的字符个数时,将源数组的num个字符拷贝过去,但不会将’\0’附加过去,即不会将’\0’拷贝过去

1.3.2strncpy的模拟实现

char* my_strncpy(char* dest, const char* sour, int num)
{
	assert(dest && sour);
	int len = strlen(sour);
	int i = 0;
	if (len < num)
	{
		for (i = 0; i < len; i++)
		{
			*(dest + i) = *(sour + i);
		}
		for (; i < num; i++)
		{
			*(dest + i) = '\0';
		}
	}
	else
	{
		for (i = 0; i < num; i++)
		{
			*(dest + i) = *(sour + i);
		}
	}
	return dest;//dest没有改变,直接返回即可
}
int main()
{
	char arr1[20] = "################";
	my_strncpy(arr1, "abcdef", 10);
	printf("%s", arr1);
	return 0;
}

答案
abcdef

答案验证了当num>源数组长度时,会添加’\0’到目标数组,‘\0’会将#覆盖,当打印时遇到’\0’停止所以只打印了abcdef。

1.3.3strncat

在这里插入图片描述
例子

int main()
{
	char arr1[20] = "abc";
	char arr2[] = "qwer";
	strncat(arr1, arr2, 5);
	printf("%s", arr1);
}

答案
abcqwer

注意
(1)strncat会将源数组的num个字符追加到目标数组,并且会偷偷追加上’\0’。
(2)当num>源数组长度时,直接将源数组追加过去(包括’\0’);当num<源数组长度时,将源数组的num个字符拷贝过去,再附加’\0’。

1.3.4strncat的模拟实现

char* my_strncat(char* dest, const char* sour, int num)
{
	assert(dest && sour);
	int len = strlen(sour);
	int i = 0;
	if (num > len)
	{
		for (i = 0; i <= len; i++)
		{
			*(dest + i) = *(sour + i);
		}
	}
	else
	{
		for (i = 0; i < num; i++)
		{
			*(dest + i) = *(sour + i);
		}
		*(dest + i) = '\0';
	}
	return dest;
}
int main()
{
	char arr1[20] = "abc";
	char arr2[] = "qwer";
	my_strncat(arr1, arr2, 5);
	printf("%s", arr1);
}

答案
abcqwer

1.3.5strncmp

在这里插入图片描述
例子

int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "abcqwe";
	int ret = strncmp(arr1, arr2, 4);
	if (ret > 0)
	{
		printf("arr1>arr2");
	}
	else if (ret = 0)
	{
		printf("arr1==arr2");
	}
	else
	{
		printf("arr1<arr2");
	}
	return 0;
}

注意
这个函数与strcmp函数类似,只是比较的不是整个数组,而是前num个字符。

1.3.6strncpy的模拟实现

int my_strncmp(const char* str1, const char* str2, int num)
{
	assert(str1 && str2);
	int i = 0;
	for (i = 0; i < num; i++)
	{
		if (*(str1 + i) != *(str2 + i))
		{
			break;
		}
	}
	return *(str1 + i) - *(str2 + i);
}
int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "abcqwe";
	int ret = my_strncmp(arr1, arr2, 4);
	if (ret > 0)
	{
		printf("arr1>arr2");
	}
	else if (ret = 0)
	{
		printf("arr1==arr2");
	}
	else
	{
		printf("arr1<arr2");
	}
	return 0;
}

1.4字符串查找函数

1.4.1strstr

在这里插入图片描述
例子

int main()
{
	char arr1[] = "abbbcdef";
	char arr2[] = "bbc";
	char* ret = strstr(arr1, arr2);
	if (NULL == ret)
	{
		printf("找不到\n");
		return 0;
	}
	printf("找到了,%s\n",ret);
	return 0;
}

答案
找到了,bbcdef

1.4.2strstr的模拟实现

strstr的模拟实现比较复杂。有以下两种情况。在这里插入图片描述
注意
(1)str1和str2作为遍历整个数组的起点,
若str1和str2对应的字符不相等,str1就++,直到两者对应字符相等。
(2)这时就需要两个指针(s1和s2)来继续遍历,分别从从str1和str2的位子开始,
(3)当s1和s2对应字符相等,就++,直到s2遇到’\0’为止,这时就可以返回str1的地址,
(4)当s1和s2对应字符随着++变得不同,就停止循环,str1继续++,寻找与str1相同的字符,
(5)特殊情况,当str1遍历到’\0’就停止循环,返回NULL;当s1和s2循环时,如果s1先遇到’\0’而s2!='\0’这时也返回NULL。

char* my_strstr(const char* str1, const char* str2)
{
	assert(str1 && str2);
	char* s1 = str1;
	char* s2 = str2;
	while (*str1)//当str1=='\0',停止循环
	{
		s1 = str1;//s1紧跟str1
		s2 = str2;//每次重新开始,把s2放回起点
		while (*s1 == *s2 && *s1!='\0' && *s2!='\0')
		{
			s1++;
			s2++;
		}
		if (*s2 == '\0')//下面两个IF不能对换,若*s1和*s2都为'\0'说明两者还是相等,直接返回即可
		{
			return str2;
		}
		if (*s1 == '\0')//能到这里说明*s2!='\0',所以直接跳出或返回NULL
		{
			break;//直接返回NULL也一样
		}
	}
	return NULL;
}
int main()
{
	char arr1[] = "abbbcdef";
	char arr2[] = "bbc";
	char* ret = my_strstr(arr1, arr2);
	if (NULL == ret)
	{
		printf("找不到\n");
		return 0;
	}
	printf("找到了,%s\n",ret);
	return 0;
}

1.5分割字符串

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 指针。

例子
在这里插入图片描述
在这里插入图片描述

1.6错误信息报告

1.6.1strerror

在这里插入图片描述
注意
当代码执行发生错误时会生成错误编号,错误编号存放在错误码errno中,所以可以直接使用errno代替errnum。使用errno需要包含头文件。
在这里插入图片描述
例子
在这里插入图片描述

1.6.2perror

在这里插入图片描述
例子

int main()
{
	FILE* pf = fopen("test.txt", "r");
	if (NULL == pf)
	{
		perror(NULL);
	}
	return 0;
}

结果
No such file or directory

注意
(1)perror的作用相当于printf加上strerror
(2)perror会自己捕获errno中的错误码转换成错误信息,再打印
(2)perror的参数如果是NULL,会直接打印错误信息;如果是程序名称的话,先打印程序名称(自定义信息),再打印错误信息在这里插入图片描述


2.字符函数

2.1字符分类函数

函数 如果他的参数符合下列条件就返回真
iscntrl 任何控制字符
isspace 空白字符:空格‘ ’,换页‘\f’,换行’\n’,回车‘\r’,制表符’\t’或者垂直制表符’\v’
isdigit 十进制数字 0~9
isxdigit 十六进制数字,包括所有十进制数字,小写字母a ~ f,大写字母A ~ F
islower 小写字母a~z
isupper 大写字母A~Z
isalpha 字母a ~ z或A ~ Z
isalnum 字母或者数字,a ~ z,A ~ Z,0 ~ 9
ispunct 标点符号,任何不属于数字或者字母的图形字符(可打印)
isgraph 任何图形字符
isprint 任何可打印字符,包括图形字符和空白字符

函数 如果他的参数符合下列条件就返回真
iscntrl 任何控制字符
isspace 空白字符:空格‘ ’,换页‘\f’,换行’\n’,回车‘\r’,制表符’\t’或者垂直制表符’\v’
isdigit 十进制数字 0~9
isxdigit 十六进制数字,包括所有十进制数字,小写字母a ~ f,大写字母A ~ F
islower 小写字母a~z
isupper 大写字母A~Z
isalpha 字母a ~ z或A ~ Z
isalnum 字母或者数字,a ~ z,A ~ Z,0 ~ 9
ispunct 标点符号,任何不属于数字或者字母的图形字符(可打印)
isgraph 任何图形字符
isprint 任何可打印字符,包括图形字符和空白字符

这些函数主要用于判断,通常与if、while等语句连用。这里挑几个作为例子。

#include<ctype.h>//头文件
int main()
{
	if (isdigit('1'))
	{
		printf("数字\n");
	}
	if (isalpha('a'))
	{
		printf("字母\n");
	}
	if (isupper('A'))
	{
		printf("大写\n");
	}
	return 0;
}

结果
数字
字母
大写

2.2字符转换函数

int tolower ( int c );//将c对应字符转换成小写,并返回其ASCII码值
int toupper ( int c );//将c对应字符转换成大写,并返回其ASCII码值

例子

#include<ctype.h>
int main()
{
	char arr[] = "hello world";
	//转换成大写
	int i = 0;
	int len = strlen(arr);
	for (i = 0; i < len; i++)
	{
		if (islower(*(arr + i)))//判断是否是小写
		{
			printf("%c", toupper(*(arr + i)));
			continue;
		}
		else
		{
			printf("%c", *(arr + i));
		}
	}
	printf("\n");
	//再转换成小写
	for (i = 0; i < len; i++)
	{
		if (isupper(*(arr + i)))//判断是否是大写
		{
			printf("%c", tolower(*(arr + i)));
			continue;
		}
		else
		{
			printf("%c", *(arr + i));
		}
	}
	return 0;
}

结果
HELLO WORLD
hello world

猜你喜欢

转载自blog.csdn.net/Zhuang_N/article/details/128823050