在实际的数据处理过程中,我们经常要处理字符和字符串,C语言中提供了一系列的函数来更加方便的操作字符和字符串,关于处理字符的函数有字符分类函数和字符转换函数,关于字符串处理的函数有strlen、strcpy、strcat、strcmp、strsrt、strtok、strerror等,学习基本语法时我们要了解这些函数。

1.字符分类函数

字符分类函数是判断一个字符属于什么类型的字符。字符分类函数的一个重要应用场景就是设置密码时要求大写字母,小写字母,特殊字符,数字的校验,使用字符分类函数可以判断你输入密码的字符类型。字符分类函数定义在ctype.h的头文件中。

下面代码具体介绍了其中一个isupper函数,其余的在官网:

 判断参数部分的c是否是大写字母,参数c可以是字符的ASCII码值,也可以是字符。如果是大写字母就返回非0的整数,如果不是大写字母,则返回0。 

#include<ctype.h>
int main()
{
	if (isupper('a'))  printf("大写字母");
	else  printf("小写字母");
	return 0;
}

练习:下列代码将会把小写字母转成大写,大写字母转成小写。

int main()
{
	char arr[] = "Hello World.";
	for (int j = 0; j < sizeof(arr) / sizeof(arr[0]); j++)
	{
		if (isupper(arr[j]))	arr[j] += 32;
		if (islower(arr[j]))	arr[j] -= 32;
		printf("%c", arr[j]);
	}
	return 0;
}

注意:第一个大写字母A的ASCII码值为65,第一个小写字母a的ASCII码值为97,所以将小写字母加32可得到相应字母的大写字母,将大写字母减32可得相应字母的小写字母。

2.字符转换函数

C语言中有两个字符转换函数,也定义在ctype.h的头文件中。

注意:不是字母的字符什么也不会改变。

上面练习的第二个版本:

int main()
{
	char arr[] = "Hello World.";
	for (int j = 0; j < sizeof(arr) / sizeof(arr[0]); j++)
	{
		if (isupper(arr[j]))	tolower(arr[j]);
		if (islower(arr[j]))	toupper(arr[j]);
		printf("%c", arr[j]);
	}
	return 0;
}

3.字符串函数:strlen

3.1strlen

strlen函数的作用是求字符串长度。统计的是从参数str指向的字符串的起始地址开始,\0之前字符串中字符的个数,参数中字符指针前面加上const的作用是防止函数中的代码通过指针修改字符串中的内容。因为字符串中字符的个数一定大于0,所以这个函数的返回类型是size_t(无符号整形,打印时使用%zu打印)。

strlen函数有两点:第一,用来求字符串的长度;第二,很在乎\0字符。strlen 函数会⼀直向后找 \0 字符直到找到为止,因此可能存在越界查找,返回值可能是随机值。第三,strlen函数返回的结果是size_t类型,运算时要注意。

下面代码是strlen求字符数组的长度会出现意想不到的结果:

下面代码是关于strlen函数返回值的陷阱:

3.2strlen函数的三种模拟实现方式

方式①:计数器方式

size_t my_strlen(const char* str)
{
	size_t count = 0;
	assert(str);
	while (*str!='\0')
	{
		count++;
		str++;
	}
	return count;
}

方式②:指针减指针方式(修改了参数的类型)

size_t my_strlen(char* str)
{
	assert(str);
	char* p = str;
	while (*p == '\0')  p++;
	return size_t(p - str);
}

方式③:递归方式(不在函数内部创建临时变量求字符串长度)

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

int main()
{
	char arr[] = "abc";
	size_t len = my_strlen(arr);
	printf("%zu\n", len);
	return 0;
}

4.字符串函数:strcpy

4.1strcpy

strcpy函数的作用是字符串拷贝,拷贝到源头字符串的\0字符为止。参数destination是指针,指向拷贝的目标空间的起始地址,source也是指针,指向拷贝的源头空间的起始地址。返回值是目标空间的起始地址。

注意:1.拷贝时源字符串必须以\0结束,因为\0作为strcpy拷贝结束的标志,拷贝的内容也包括源字符串的\0。2.destination指向的目标空间必须足够大,确保能放得下源字符串的内容。3.destination指向的目标空间必须是可修改的。4.strcpy拷贝的结果是将源头空间的起始地址存放的数据依此覆盖到目标空间的起始地址的后面。源头空间的数据只会拷贝到\0结束,\0后面的数据不会被拷贝。

示例代码①:

int main()
{
	/*将数组arr1的内容拷贝到arr2*/
	char arr1[] = "hello world";
	char arr2[20] = { 0 };
	//arr2 = arr1;//因为数组名是地址,地址是一个编号,是常量值,不能被修改
	char* ret = strcpy(arr2, arr1);
	printf("%s\n", ret);
	return 0;
}

示例代码②:destination指向的目标空间太小造成的结果:

示例代码③:destination指向的目标空间不可修改造成的结果:

示例代码④:strcpy拷贝的结果是将源头空间的数据依此覆盖到目标空间起始地址的后面。并且源头空间的数据只会拷贝到\0结束,\0后面的数据不会被拷贝:

4.2strcpy函数的模拟实现

#include <assert.h>
char* my_strcpy(char* destination,const char* source)
{
    assert(destination && source);
	char* dest = destination;
	while (*dest++ = *source++){}
    //*dest = *source;  dest++;  source++;
	//while循环每次判断的是ascii码值
    //在最后一次将\0赋值给dest后ascii码值变为0,循环结束
    return destination;
}

int main()
{
	/*将数组arr1的内容拷贝到arr2*/
	char arr1[] = "hello world\0abc";
	char arr2[50] = "xxxxxxxxxxxxxxxxxxxxxxxx";
	char* ret = my_strcpy(arr2, arr1);
	printf("%s\n", ret);
	return 0;
}

5.字符串函数:strcat

5.1strcat

strcat函数的作用是字符串拼接,将source指向的源头字符串起始地址到\0字符为止的所有字符追加到destination指向的目标字符串的末尾。参数destination是指针,指向追加的目标空间的起始地址,source也是指针,指向待追加的源头空间的起始地址。返回值是目标空间的起始地址。

注意:1.拼接时源字符串必须以\0结束,因为\0作为strcat拼接结束的标志,拼接的内容也包括源字符串的\0。2.目标字符串也必须有\0,否则无法知道从何处开始拼接。3.destination指向的目标空间必须足够大,确保能放得下要拼接的字符串。4.destination指向的目标空间必须是可修改的。

代码①:示例代码:

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

代码②:拼接时源字符串必须以\0结束,因为\0作为strcat拼接结束的标志,拼接的内容也包括源字符串的\0。拼接时从目标字符串的\0开始拼接。

5.2strcat函数的模拟实现

#include <assert.h>
char* my_strcat(char* destination, const char* source)
{
    assert(destination && source);
	char* dest = destination;
	while (*dest++) {}
	while (*dest++ = *source++) {}
	return destination;
}
int main()
{
	char arr1[20] = "hello";
	char arr2[] = "world";
	char* ret = strcat(arr1, arr2);
	printf("%s\n", ret);
	return 0;
}

6.字符串函数:strcmp

6.1strcmp

strcmp函数的作用是字符串比较,比较str1和str2指向的字符串,从两个字符串的第一个字符开始比较,如果两个字符的ASCII码值相同就比较下一个字符,直到遇到两个不相等的字符或者字符串结束。参数str1、str2是指针,分别指向两个字符串的起始地址。返回值遵循下图规定。

比如abcdef和abq相比:字符a和a比,相等,比较下一个;字符b和b比,相等,比较下一个;字符c和q比,c的ascii码值小于q,则第一个字符串小于第二个字符串。再比如abcd和abc相比:字符a和a比,相等,比较下一个;字符b和b比,相等,比较下一个;字符c和c比,相等,比较下一个;字符d和\0比,d的ascii码值大于\0,则第一个字符串大于第二个字符串。

注意:1.

代码①:示例代码:

int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "abq";
	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");
	return 0;
}

代码②:也可以把字符串直接放到函数中:

int main()
{
	int ret = strcmp("abcdef", "abq");
	if (ret > 0)  printf("arr1 > arr2\n");
	else if (ret < 0)  printf("arr1 < arr2\n");
	else  printf("arr1 = arr2\n");
	return 0;
}

6.2strcmp函数的模拟实现

#include <assert.h>
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 if (*str1 < *str2) return -1;
}

int main()
{
	int ret = strcmp("abcdef", "abq");
	if (ret > 0)  printf("arr1 > arr2\n");
	else if (ret < 0)  printf("arr1 < arr2\n");
	else  printf("arr1 = arr2\n");
	return 0;
}

总结:

7.字符串函数:strncpy

strncpy函数的作用是字符串拷贝,将source所指空间中的字符串拷贝到destination所指的空间。对于source所指空间中的字符串最多能拷贝num个字符。如果不够num个,后面继续补上\0。

示例代码:

8.字符串函数:strncat

strncat函数的作用是字符串拼接,将source指向的源头字符串起始地址到第num个字符为止的所有字符追加到destination指向的目标字符串的末尾。

注意:strncat函数追加完num个字符后会再给后面补上一个\0字符,当num的个数大于源字符串的个数时也只会追加一个\0:

9.字符串函数:strncmp

strncmp函数的作用是字符串比较,比较str1和str2指向的字符串,从两个字符串的第一个字符开始比较,如果两个字符的ASCII码值相同就比较下一个字符,最多比较num个字符,如果提前发现不一样,就提前结束。如果num个字符都相等,就返回0。

10.字符串函数:strstr

10.1strstr

strstr函数的作用是在一个字符串中查找子字符串,查找str2指向的字符串在str1指向的字符串中第一次出现的位置。参数str1指向被查找的字符串,参数str2指向要查找的字符串。如果str1指向的字符串中存在str2指向的字符串那么函数返回字符串str2在字符串str1中第一次出现的位置;如果str1指向的字符串中不存在str2指向的字符串那么函数返回NULL(空指针)。

示例代码:

int main()
{
	char arr1[] = "abcdefabcdef";
	char arr2[] = "def";
	char* p = strstr(arr1, arr2);
	if (p != NULL)  printf("找到了,%s\n", p);
	else printf("没找到\n");
	return 0;
}

10.2strstr函数的模拟实现

const char* my_strstr(const char* str1, const char* str2)
{
	if (*str2 == '\0')  return str1;  //特殊情况

	const char* s = str1;
	while (*s != '\0')
	{
		const char* p1 = s;
		const char* p2 = str2; 
		if (*p1 == *p2)
		{
			//p1 != '\0'防止内循环越界  eg:str1 = abc; str2 = abcd;
			while (*p1 != '\0' && *p2 != '\0' && *p1 == *p2)
			{
				p1++;
				p2++;
			}
			if(*p2 == '\0')  return p1;
		}
		s++;
	}
	return NULL;
}

int main()
{
	char arr1[] = "abcdefabcdef";
	char arr2[] = "def";
	const char* p = my_strstr(arr1, arr2);
	if (p != NULL)  printf("找到了,%s\n", p);
	else printf("没找到\n");
	return 0;
}

11.字符串函数:strtok

应用场景:对于一个IP地址192.168.100.101获取其中的每一段,将这个字符串以点为分隔符进行切割,切割出来每一段,strtok主要作用就是利用分隔符切割字符串。

strtok函数的作用是根据delimiters参数中指定的分隔符,将str指向的字符串分割成多个子字符串。strtok会在原始字符串中直接插入\0字符替换分隔符的位置,原始字符串会被直接修改。

参数:首次调用strtok函数时参数str指向被分割的字符串,后续调用时传入NULL表示继续分割同一个字符串;参数delimeter也指向字符串,指向的字符串是由所有可能的分隔符放到一起组成的。

返回值:切割成功时返回当前子字符串的指针,没有更多子字符串时返回NULL。

使用过程:

示例代码:实际代码中一般这样写:

int main()
{
	char arr[] = "tanhao521125@qq.com";
	char deli[] = "@.";
	char buff[20] = {};
	strcpy(buff, arr);

	char* p = NULL;
	for (p = strtok(buff, deli); p != NULL; p = strtok(NULL, deli))
	{
		printf("%s\n", p);
	}

	return 0;
}

注意:多个连续的分隔符在一起时会被视为单个分隔符,不会返回空字符串。

12.字符串函数strerror

使用场景:C语言的库函数在使用时,如果发生了错误,会将一个错误码记录到errno的变量中,如果只是得到一个错误码无法真正了解错误信息,但是将错误码传入到strerror函数中就能输出该错误码对应的信息。

strerror函数的作用是可以把参数errnum错误码对应的错误信息的字符串首地址返回来。

在C语言标准库的实现中规定了一些错误码,放在errno.h这个头文件中说明的,C语言程序启动的时候就会用一个全局变量errno来记录程序的错误码,程序启动的时候errno是0,没有错误。当使用标准库中的函数时发生了某种错误,就会将错误码存放到errno中,将errno的值放入strerror函数中就能知道错误对应的信息。注意:strerror只对标准库中的函数发生错误后进行错误码转换,使用errno这个全局变量时需要包含头文件errno.h。

随便的一些例子:

示例代码:使用fopen函数打开一个不存在的文件。

补充:perror函数相当于printf+strerror

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

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐