字符函数和字符串函数

目录

字符函数

islower

ispower

大小写字母转换函数

字符串函数

strlen使用和模拟实现

strcpy

strncpy

strcat

strncat

strcmp

strncmp

strstr

strtok

strerror

perror


字符函数

使用字符函数需要包含头文件 

#include <ctype.h>

islower

int islower( int c );

  1. islower可以判断参数部分的c是否是小写字母
  2. 如果是小写字母就返回非0值,如果不是小写就返回0
  3. 字符本质是整数,每个字符都有对应的ASCII码

将小写字母转换成大写字母

#include <stdio.h>
#include <ctype.h>

int main()
{
	char arr[] = "Test String";
	int sz = sizeof(arr) / sizeof(arr[0]);
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		int ret = islower(arr[i]);
		if (ret)
		{
			arr[i] = arr[i] - 32;
		}
	}
	printf("%s\n", arr);
	return 0;
}

ispower

int isupper( int c );

  1. isupper可以判断参数部分的c是否是大写字母
  2. 如果是大写字母就返回非0值,如果不是大写就返回0

大小写字母转换函数

  1. int tolower( int c );  将大写字母转化成小写字母
  2. int toupper( int c ); 将小写字母转换成大写字母

将小写字母转换成大写字母

int main()
{
	char arr[] = "Test String";
	int sz = sizeof(arr) / sizeof(arr[0]);
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		int ret = islower(arr[i]);
		if (ret)
		{
			arr[i] = toupper(arr[i]);
		}
	}
	printf("%s\n", arr);
	return 0;
}

字符串函数

使用字符串函数需要包含的头文件

#include <string.h>

strlen使用和模拟实现

size_t strlen ( const char * str );

  1. 字符串以 '\0' 作为结束标志,strlen函数返回的是在字符串中 '\0' 前面出现的字符个数(不包含 '\0' )
  2. 参数指向的字符串必须要以 '\0' 结束
  3. 注意函数的返回值为size_t,是无符号的

判断输出的结果

int main()
{
	const char* str1 = "abcdef";
	const char* str2 = "bbb";
	if (strlen(str2) - strlen(str1) > 0)
	{
		printf("str2>str1\n");
	}
	else
	{
		printf("srt1>str2\n");
	}
	return 0;
}

运行结果

  1. strlen(str2)返回结果是无符号的3
  2. strlen(str1)返回结果是无符号的6
  3. 无符号数减无符号数的值也是无符号的
  4. 3 - 6 = -3 ,我们把-3当成无符号数
  5. 有符号的-3补码是11111111 11111111 11111111 11111101,最高位的1说明这个数是负数
  6. 无符号的-3就是把最高位的1当成的数值位,所以无符号的-3的原反补都是       11111111 11111111 11111111 11111101,转成10进制就是4294967293

有人肯定会有这样的疑问:为啥我看过一段代码就是strlen(str2) - strlen(str1)的值输出的是-3,而不是4294967293

原因是:%d打印的是有符号的十进制,所以我们把无符号的整形解读成有符号数,所以正确的是要用%u来打印,%u打印的是无符号的整数,是以十进制的形式来输出

strlen的模拟实现\

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

int main()
{
	char arr1[] = "hello world";
	printf("%zd\n", my_strlen(arr1));
	return 0;
}

strcpy

字符串拷贝函数

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

将源头的数据拷贝到目标空间去

destination:目的地

source:源头

使用strcpy的注意事项

  • 源字符串必须以 '\0' 结束。
  • 会将源字符串中的 '\0' 拷贝到目标空间。
  • 目标空间必须足够大,以确保能存放源字符串。
  • 目标空间必须可修改。
  • 函数返回的是目标空间的起始地址
int main()
{
	char arr1[20] = { 0 };
	char arr2[] = "hello world";
	strcpy(arr1, arr2);
	printf("%s\n", arr1);
	return 0;
}

注意:如果目标空间不能修改,那么就会报错

strcpy的模拟实现

char* my_strcpy(char* dest, const char* src)
{
	assert(dest && src);
	char* ret = dest;
	//当*src是'\0'时,我们就把'\0'传给*dest,while判断语句就为0
	//while循环退出时dest在向后走一步,src同理
	while (*dest++ = *src++)
	{
		;
	}
	return  ret;
}
int main()
{
	char arr1[20] = { 0 };
	char arr2[] = "hello world";
	my_strcpy(arr1, arr2);
	printf("%s\n", arr1);
	return 0;
}

strncpy

更安全的字符串拷贝

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

destination:目的地

source:源头

num:要拷贝的字符个数

strncpy使用说明

  1. 拷贝num个字符从源字符串到目标空间
  2. 如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加'\0',直到num个。
  3. 如果源字符串中有6个字符,但是只拷贝3个字符过去,那么就不会额外多追加'\0'

strncpy的模拟实现

char* my_strncpy(char* dest, const char* sour, size_t num)
{
	assert(dest && sour);
	int i = 0;
	char* ret = dest;
	while (num--)
	{
		*dest++ = *sour++;
		if (*sour == '\0')
		{
			while (num--)
			{
				*dest++ = *sour;
			}
			return ret;
		}
	}
	return ret;
}

int main()
{
	char arr1[] = "hello worldxxxxxxxx";
	char arr2[] = "hello bit";
	char* ret = my_strncpy(arr1, arr2, 11);
	printf("%s\n", arr1);
	return 0;
}

strcat

字符串追加

char *strcat( char *strDestination, const char *strSource );

strDestination:目标空间

strSource:源头

将源头的数据追加到目标空间'\0'的后面

使用strcat的注意事项

  1. 源字符串必须以 '\0' 结束。
  2. 目标字符串中也得有 '\0' ,否则没办法知道追加从哪里开始。
  3. 目标空间必须有足够的大,能容纳下源字符串的内容。
  4. 目标空间必须可修改。
  5. 函数返回的是目标空间的起始地址

strcat的模拟实现

char* my_strcat(char* dest, const char* src)
{
	assert(dest && src);
	char* ret = dest;
	while (*dest)
	{
		dest++;
	}
	while (*dest++ = *src++)
	{
		;
	}
	return ret;
}

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

思考下自己给自己追加可以吗?

结果:死循环+越界访问,因为两个指针指向的都是同一块空间,会把'\0'给覆盖掉(自己模拟的实现不了,但是库里面的strcat是可以的)。自己给自己追加最好还是用strncat。

strncat

更安全的字符串追加函数

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

destination:目标空间

source:源头

num:要追加的字符个数

strncat的使用说明

  1. 将source指向字符串的前num个字符追加到destination指向的字符串末尾,再追加⼀个 '\0' 字符
  2. 如果source指向的字符串的长度小于num的时候,只会将字符串中到'\0'的内容追加到destination指向的字符串末尾,'\0'追加后不会再额外追加其他

strncat的模拟实现

char* my_strncat(char* dest, const char* sour, size_t num)
{
	assert(dest && sour);
	char* ret = dest;
	while (*dest)
	{
		dest++;
	}
	while (num--)
	{
		*dest++ = *sour++;
		if (*sour == '\0')
		{
			*dest = *sour;
			return ret;
		}
	}
	*dest = '\0';
	return ret;
}

int main()
{
	char str1[100] = "To be\0\\123456789xxx";
	char str2[20] = "or not to be";
	my_strncat(str1, str2, 5);
	printf("%s\n", str1);
	return 0;
}

strcmp

字符串比较函数

int strcmp( const char *string1, const char *string2 );

比较的是对应位置上的字符串大小,小的字符所在的字符串小于另外一个字符串

注意:比较的不是字符串的长度

标准规定

  1. 第一个字符串大于第二个字符串,则返回大于0的数字
  2. 第一个字符串等于第二个字符串,则返回0
  3. 第一个字符串小于第二个字符串,则返回小于0的数字
  4. 比较两个字符串中对应位置上字符ASCII码值的大小

strcmp的模拟实现

int my_strcmp(const char* str1, const char* str2)
{
	assert(str1 && str2);
	while (*str1 == *str2)
	{
		str1++;
		str2++;
	}
	return *str1 - *str2;
}

int main()
{
	char arr1[] = "abcd";
	char arr2[] = "abcf";
	int ret = my_strcmp(arr1, arr2);
	if (ret > 0)
	{
		printf("%s大于%s\n", arr1, arr2);
	}
	else if (ret == 0)
	{
		printf("%s等于%s\n", arr1, arr2);
	}
	else
	{
		printf("%s小于%s\n", arr1, arr2);
	}
	return 0;
}

strncmp

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

较str1和str2的前num个字符,如果相等就继续往后比较,最多比较num个字母,如果提前发现不一样,就提前结束,大的字符所在的字符串大于另外一个。如果num个字符都相等那就返回0

strstr

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

返回str2指向的字符串在str1指向字符串中第一次出现的位置

如果str2不是str1的一部分,则返回 null 指针

char* my_strstr(const char* str1, const char* str2)
{
	assert(str1 && str2);
	//如果*str2只有'\0'那就输出str1指向的字符串
	if (*str2 == '\0')
	{
		return (char*)str1;
	}
	const char* cur1 = str1;
	const char* cur2 = str2;
	while (*str1)
	{
		cur1 = str1;
		cur2 = str2;
		while (*cur1 == *cur2 && *cur2 != '\0')
		{
			cur1++;
			cur2++;
		}
		if (*cur2 == '\0')
		{
			return (char*)str1;
		}
		str1++;
	}
	return NULL;
}

int main()
{
	char arr1[] = "abaacaaaddf";
	char arr2[] = "aaa";
	char* ret = my_strstr(arr1, arr2);
	if (ret != NULL)
	{
		printf("%s\n", ret);
	}
	else
	{
		printf("找不到");
	}
	return 0;
}

strtok

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

例子:zhangsan@qq.com

  • sep参数指向一个字符串,定义了用作分隔符的字符集合,例如zhangsan和qq和com都被'@'’和'.'给分割了,所以我们把这些分割符集合成字符串"@."
  • 第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标记。例如zhangsanqqcom都属于一个标记,都是被"@."这两个分割符给分割的,所以str就指向了"zhangsan@qq.com"
  • strtok函数找到str中的下一个标记,并将其用'\0'结尾,返回⼀个指向这个标记的指针。(注: strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串⼀般都是临时拷贝的内容并且可修改)例子:"zhangsan@qq.com",str会找到一个被分割符分割的标记,并把分割符改成'\0',并把标记的起始地址返回。也就是说一开始str找到分割符是'@',所以就把'@'改成了'\0',并把"zhangsan"起始地址z的地址给返回
  • strtok函数的第一个参数不为 NULL ,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置。也就是说第一个参数不为NULL只需要执行一次就行了。例子:"zhangsan@qq.com",这里面有3个标记,但是第一个标记就是"zhangsan",保存他在字符串中的位置就是保存分割符的位置,这里就是保存把'@'改成'\0'的位置,这样方便下一次寻找标记
  • strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标记。这里就是从保存把'@'改成'\0'的地址向后开始找分割符,找到了就把分割符修改成'\0',并保存分割符的位置,并且把标记的起始地址返回。例子:"zhangsan\0qq.com",我们从'\0'的位置开始找分割符'.',找到了就把'.'修改成'\0',并且保存'\0'的地址,并且返回"qq"的起始地址
  • 如果字符串中不存在更多的标记,则返回 NULL 指针。
int main()
{
	char arr1[] = "zhangsan@qq.com";
	char* p = "@.";
	char arr2[30] = { 0 };
	strcpy(arr2, arr1);
	char* ptr = NULL;
	for (ptr = strtok(arr2, p); ptr != NULL; ptr = strtok(NULL, p))
	{
		printf("%s\n", ptr);
	}
	return 0;
}

strerror

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

char * strerror ( int errnum );

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

#include <errno.h>
#include <string.h>
#include <stdio.h>

int main()
{
	FILE* pFile;
	//在这个程序的文件夹中打开unexit.txt的文件,用读的形式
	pFile = fopen("unexit.txt", "r");
	//如果pFile是NULL就说明打开失败
	//使用库函数发生的错误,就会把对应的错误码会放在全局变量errno
	if (pFile == NULL)
	{
		printf("Error oprning file unexit.txt:%s\n", strerror(errno));
	}
	else
	{
		printf("打开文件成功\n");
	}
	return 0;
}

注意:

如果第一个库函数出错了,第二个库函数也出错了,那它反应的就是第二个错误码对应的错误信息。每次发生错误都会更新全局的errno,使用要及时判断

perror

perror函数可以直接将错误的信息打印出来。perror函数打印完参数部分的字符串后,会在额外打印一个冒号和空格,再打印错误信息。

  • 14
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值