【C进阶】 字符串函数和字符分类函数

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

家人们欢迎来到小姜的世界<<点此>>传送门 这里有详细的关于C/C++/Linux等的解析课程家人们赶紧冲鸭
客官码字不易来个三连支持一下吧关注我不迷路

字符串函数和字符分类函数


前言

本文的重点是以下几个字符函数和字符串函数
在这里插入图片描述
C语言中对字符和字符串的处理很是频繁但是C语言本身是没有字符串类型的字符串通常放在常量字符串中或者字符数组中。字符串常量适用于那些对它不做修改的字符串函数.


一、strlen

在之前的博客中细细分析过大家可以先去看一看实现的形式下面的各个形式也同样是概况与拓展。
传送门<<点此>>

一介绍

1.字符串已经 ‘\0’ 作为结束标志strlen函数返回的是在字符串中 ‘\0’ 前面出现的字符个数不包含 ‘\0’ )。
2.参数指向的字符串必须要以 ‘\0’ 结束。
3.注意函数的返回值为size_t是无符号的
4.学会strlen函数的模拟实现

建议大家使用size_t表示无符号整型
如下代码事例

#include<stdio.h>
int main() {
	if (strlen("abc") - strlen("abcdef")) {
		printf(">\n");
	}
	else {
		printf("<=\n");
	}
	return 0;
}

大家可以想一想上面这串代码输出的应该是什么3-6=-3应该输出的是<=对吧可是我们编译看一下结果
在这里插入图片描述

这怎么输出的是>原来是strlen的返回值是size_t在计算机内部存储的是二进制的补码而-3转化称为补码以后存储计算机中可是计算机在取出的时候拿到的是无符号整形不看符号位的所以是一个很大很大的正数所以是>。

二模拟实现

我们之前也了解过模拟实现那我们直接给代码其中有三种模拟形式第一种就是常规的解法是用指针从开始往后一直指到’\0’第二种就是利用递归思想第三种就是指针减指针的方法。

#include<assert.h>
#include<stdio.h>

//size_t == typedef unsigned int size_t
//法3
//指针-指针
size_t my_strlen(const char* str) {
	assert(str);
	const char* start = str;//定义首指针
	while (*str) {
		str++;
	}
	return str - start;//计算长度
}

//法2
//递归思想
size_t my_strlen(const char* str) {
	assert(str);
	if (*str != '\0')
		return 1 + my_strlen(str + 1);
	else
		return 0;
}

//法1
//常规思路往后加1找大小
size_t my_strlen(const char* str) {
	assert(str);
	int count = 0;
	while (*str) {
		str++;
		count++;
	}
	return count;
}

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

二、strcpy

在之前的博客中细细分析过大家可以先去看一看实现的形式下面的各个形式也同样是概况与拓展。
<<点此>>传送门

一介绍

在这里插入图片描述
1.源字符串必须以 ‘\0’ 结束。 ---- 将strSource中一直到’\0’之前的字符以及’\0’拷贝到strDestination所以一定要有’\0’。
2.会将源字符串中的 ‘\0’ 拷贝到目标空间。 — 从strSource中的所有字符包括’\0’都拷贝上去。
3.目标空间必须足够大以确保能存放源字符串。 — 被拷贝的空间一定要是足够大的空间能够存放拷贝的字符串。
4.目标空间必须可变。— 如果给的是一个常量字符串那肯定用不了strcpy了只有数组是可变的才能用strcpy。

二模拟实现

直接上代码

#include<stdio.h>
#include<assert.h>
char* my_strcpy(char* dest, const char* src) {
	assert(dest && src);
	char* ret = dest;
	while (*dest++ = *src++) {
		;
	}
	return dest;
}

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

三、strcat

一介绍

大家肯定可以发现的是strcpy和strcat两个库函数是一模一样的一个是拷贝过去另一个是追加字符串。
在这里插入图片描述
先来了解一下这个函数的构成
在这里插入图片描述
那根据这个函数的构成我们进行写一下追加吧
在这里插入图片描述
既然要追加是怎么追加的呢是从目标指针的’\0’开始追加的那也就是说两个字符串数组都是需要’\0’的被追加的字符串能有个尾巴被追加追加过去的那个字符串能找到尾巴追加过去并在把’\0’拷贝过去以后能结束停止拷贝如下图
在这里插入图片描述
所以总结一下
1.源字符串必须以 ‘\0’ 结束。
2.目标空间必须有足够的大能容纳下源字符串的内容。
3.目标空间必须可修改。

二模拟实现

我们在了解了这些概念了以后我们能够进行模拟实现它第一步是需要将目标指针移动到它的’\0’处再进行追加是不是很简单呢那我们直接给出代码

#include<stdio.h>
#include<assert.h>
#include<string.h>
char* my_strcat(char* dest, char* src) {
	assert(dest && src);
	char* ret = dest;
	//1.找到目标空间的'\0'
	while (*dest != '\0') {
		dest++;
	}
	//2.追加
	while (*dest++ = *src++) {
		;
	}
	return ret;
}

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

三有趣的知识

strcat库函数能够进行追加字符串那它自己追加自己呢答案是死循环如下图解释
在这里插入图片描述
当我们将dest指针往后移动找’\0’的时候发现找到了挺开心的src和dest两个指针一起往后运动当src指针运动到f的尾部想这下子我可以美美完成工作睡大觉了可是发现这咋没’\0’呢震惊了跟自己说不灰心往后找找肯定有的但是找来找去发现没有’\0’dest指针也纳闷了这娃子咋让我一直走不带停歇的呢系统崩溃了这时候系统和这两个指针说你们虽然在完成任务可是你看dest你把人src的\0给覆盖掉了把墙给拆了src找不到南墙回不了头呀这时候我们就知道了dest在往后找完后进行覆盖的时候首当其冲把’\0’给覆盖了这怪不得会死循环。


四、strcmp

在之前的博客中细细分析过大家可以先去看一看实现的形式下面的各个形式也同样是概况与拓展。
传送门<<点此>>

一介绍

标准规定
第一个字符串大于第二个字符串则返回大于0的数字
第一个字符串等于第二个字符串则返回0
第一个字符串小于第二个字符串则返回小于0的数字
在这里插入图片描述
简单实现
在这里插入图片描述

ps:在VS环境下>返回1<返回-1=返回0。

比较形式
在这里插入图片描述

二模拟实现

指针相等则两个指针往后移动但不要忘记的是当在函数内部指针是等于’\0’的时候是这两个字符串相等而跳出循环发现指针解引用哪个ASCII码值大就是那个大。

//VS环境下
#include<stdio.h>
#include<assert.h>
int my_strcmp(const char* str1, const char* str2) {
	assert(str1 && str2);
	while (*str1 == *str2) {
		if (*str1 == '\0' && *str2 == '\0') {//两个字符串相等的时候
			return 0;
		}
		str1++;
		str2++;
	}
	if (*str1 > *str2)
		return 1;
	else
		return -1;
}
int main() {
	char arr1[] = "abcdef";
	char arr2[] = "abq";
	int ret = my_strcmp(arr1, arr2);
	printf("%d\n", ret);
	return 0;
}

改进

#include<stdio.h>
#include<assert.h>
int my_strcmp(const char* str1, const char* str2) {
	assert(str1 && str2);
	while (*str1 == *str2) {
		if (*str1 == '\0' && *str2 == '\0') {//两个字符串相等的时候
			return 0;
		}
		str1++;
		str2++;
	}
	/*if (*str1 > *str2)
		return 1;
	else
		return -1;*/
	return *str1 - *str2;
}
int main() {
	char arr1[] = "abcdef";
	char arr2[] = "abq";
	int ret = my_strcmp(arr1, arr2);
	printf("%d\n", ret);
	return 0;
}

五、strncpy

一介绍

我们在进行完strcpy的讲解相信大家对这个函数有了比较简单的了解但那些函数长度不受限制你可以随意拷贝字符串是非常不安全的那我们即将介绍strncpy这个库函数是长度受到限制的库函数。
1.拷贝num个字符从源字符串到目标空间。
2.如果源字符串的长度小于num则拷贝完源字符串之后在目标的后边追加0直到num个。
以下是简单介绍
在这里插入图片描述
有了以上的概念我们使用这个函数试一试吧
在这里插入图片描述
这拷贝的是hel过去但后面还有那么多xxx没有被替代这是很好理解的我只需要改变这几个字符其他字符不改变呀
那可能大家想了那我调皮一下我给的数比字符串长度要长呢输出的是什么那万变不离调试我们试一试调试吧
在这里插入图片描述
这已经有\0了那就后面的都没了到\0之前即可。

二模拟实现

只要多一个num–即可。

#include<stdio.h>
#include<assert.h>
char* my_strncpy(char* dest, const char* src, size_t num) {
	assert(dest && src);
	char* ret = dest;
	while (num--) {
		*dest++ = *src++;
	}
	return ret;
}
int main() {
	char arr1[20] = "xxxxxxxx";
	char* ret = my_strncpy(arr1, "hello world!", 3);
	printf("%s\n", arr1);
	return 0;
}

六、strncat

一介绍

在介绍完strcat以后大家肯定对追加字符串有了较为基础的了解那我们再来讲解一下strncat吧
在这里插入图片描述
有了上面的概念我们来简单使用一下它吧
在这里插入图片描述
那前面我们讲了strncpy不会增加个\0那strncat呢它是会增加一个\0的我们进入调试看看
在这里插入图片描述

二模拟实现

不仅仅要加个num–循环更要知道的是\0后面的值都舍弃掉。

#include<stdio.h>
#include<assert.h>
char* my_strncat(char* dest, char* src, size_t num) {
	assert(dest && src);
	char* ret = dest;
	//1.找到目标空间的'\0'
	while (*dest != '\0') {
		dest++;
	}
	//2.追加
	while (num--) {
		*dest++ = *src++;
	}
	*dest = 0;//\0后面的值都不要'\0'的ASCII码值为0
	return ret;
}
int main() {
	char arr[20] = "hello\0xxxxxxx";
	char* ret = my_strncat(arr, "world!", 4);
	printf("%s\n", arr);
	return 0;
}

七、strncmp

一介绍

在介绍完strcmp后这个strncmp仅仅比strcmp多了个数那我们先了解再实现吧
在这里插入图片描述
有了上面的概念我们来简单使用一下它吧
在这里插入图片描述

二模拟实现

依旧是用num–循环。

#include<stdio.h>
#include<assert.h>
int my_strncmp(char* str1, char* str2, int num) {
	assert(str1 && str2);
	while (num--) {
		if (*str1 == *str2) {
			str1++;
			str2++;
		}
		else {
			if (*str1 > *str2) {
				return 1;
			}
			else {
				return -1;
			}
		}
	}
	return 0;
}
int main() {
	char arr1[] = "abcdef";
	char arr2[] = "abqwq";
	int ret = my_strncmp(arr1, arr2, 3);
	printf("%d\n", ret);
	return 0;
}

八、strstr

一介绍

strstr库函数简单来讲就是找在第一个字符串找有没有第二个字符串的地址有则返回第一个出现的字符串的位置无则返回空指针。
在这里插入图片描述
那有了上面的概念我们进行简单实现吧
在这里插入图片描述

二模拟实现

我们发现是有两种情况的第一种情况是第一次往后找字符串就找到了然后与第二个字符串匹配第二种情况是找第一次不匹配找第二次才匹配。
在这里插入图片描述
那我们先来分看一下第一种情况的实现吧
在这里插入图片描述

再看一下第二种情况吧
思路是用两个指针代替原本的指针运动后续元素不匹配那就重新跳回去继续找即可。
在这里插入图片描述
解题代码如下

#include<stdio.h>
#include<assert.h>
char* my_strstr(const char* str1, const char* str2) {
	assert(str1 && str2);
	//空字符串找不到但输出是第一个字符串
	if (*str2 == '\0') {
		return str1;
	}
	//找替代的指针让它们去走
	const char* sp1 = str1;
	const char* sp = str2;
	//cp去储存每次str1与str2两个一样的地址
	const char* cp = str1;

	while (*cp) {
		//碰到一样元素后续不一样sp1先回来再往后走一位
		//找不到sp永远是处在str2首地址的位置
		sp1 = cp;
		sp = str2;
		while (*sp1!='\0' && *sp!='\0' && *sp1 == *sp) {//判断都不是走到末尾
			sp1++;
			sp++;
		}
		if (*sp == '\0') {//第二个字符串走到头
			return cp;
		}
		cp++;
	}
	return NULL;

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

九、strtok

一介绍

这是个很怪的库函数简单来说就是分隔符的集合下面是strtok的简单介绍我也会用代码的形式一步步解析定义
在这里插入图片描述
在这里插入图片描述
我们要了解的是这个strtok是会永久替换替换符的那我们就需要再加一个数组去当个影子参与替换。
在这里插入图片描述
当我们找到第一个字符串以后也是需要往后找其他的字符串那这个有讲究了是需要NULL空指针当做第一个元素的因为strtok会记下分割符转换成为’\0’的位置。
在这里插入图片描述
代码

#include<stdio.h>
#include<string.h>
int main() {
	char arr[] = "hello/world,nihao/wendang";//分隔符是,/
	char* p = ",/";
	char strDest[30] = { 0 };
	strcpy(strDest, arr);
	char* ret = strtok(strDest, p);
	printf("%s\n", strDest);
	printf("%s\n", arr);
	ret = strtok(NULL, p);
	printf("%s\n", ret);
	ret = strtok(NULL, p);
	printf("%s\n", ret);
	ret = strtok(NULL, p);
	printf("%s\n", ret);
	return 0;
}

这写的有点费时间如果有几百个串是不是很浪费时间那就需要进行循环了

#include<stdio.h>
#include<string.h>
int main() {
	char arr[] = "hello/world,nihao/wendang";//分隔符是,/
	char* p = ",/";
	char strDest[30] = { 0 };
	strcpy(strDest, arr);
	char* ret = NULL;
	for(ret = strtok(strDest, p); ret != NULL; ret = strtok(NULL, p)) {
		printf("%s\n", ret);
	}

	return 0;
}

在这里插入图片描述

二模拟实现

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

char* my_strtok(char* strToken, const char* strDelimit){
	assert(strDelimit && strDelimit);
	static char* s1 = NULL;//静态变量之后再进来的时候可以接着原本的值使用
	static char* s2 = NULL;
	static int len1 = 0;
	static int count = 0;
	int len2 = 0;
	int i = 0;
	if (strToken) { //第一个字符不等于'\0'             
		s1 = strToken;                          
		len1 = strlen(strToken);
		len2 = strlen(strDelimit);
		 while(*strToken != '\0'){//解引用以后拿到的字符不等于'\0'
			for (i = 0; i < len2; i++) {
				if (i == 0){
					count++;
				}
				if (*strToken == *(strDelimit + i)){
					*strToken = '\0'; //找到了那个分隔符就将这个分隔符内容赋为0           
					s2 = strToken;               
					return s1;//返回头指针
				}
			}
			strToken++;
		 }
	}
	else{
		s1 = s2 + 1; //s1去移动s2不用运动                  
		len2 = strlen(strDelimit);
		strToken = s1;                           
		while(*strToken != '\0'){
			for (i = 0; i < len2; i++){
				if (i == 0){
					count++;
				}
				if (*strToken == *(strDelimit + i)){
					*strToken = '\0';   //找到了那个分隔符就将这个分隔符内容赋为0         
					s2 = strToken;
					return s1;
				}
			}
			strToken++;
		}
	}
	if (count > len1){//要是
		return NULL;
	}
	return s1;
}

int main(){
	char arr1[] = "hello/world,nihao/wendang";
	char arr2[] = "/,";
	char* str = NULL;
	for (str = my_strtok(arr1, arr2); str != NULL; str = my_strtok(NULL, arr2)){//进入循环不断的返回
		printf("%s\n", str);
	}
	return 0;
}

十、strerror

一介绍

在这里插入图片描述
C语言的库函数在运行的时候如果发生错误就会将错误码存放在一个变量中这个变量是errno错误码是一些数字所以我们需要将错误码翻译成错误信息返回错误码所对应的错误信息。
举个例子
在这里插入图片描述
如图这个函数是用于返回errnum所对应的错误信息的而这些错误信息是C语言中库函数报错的时候的错误码。

二使用

在我们使用这个库函数的时候我们需要先认识了解一下fopen库函数在这里插入图片描述
这个有点提前了那也需要在这里说明一下如下代码

#include<stdio.h>
#include<errno.h>
int main() {
	//打开文件
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL) {
		printf("%s\n",strerror(errno));//errno是用来保存错误信息strerror是用来进行译码
		return 1;
	}
	//读文件
	//关闭文件
	fclose(pf);//释放
	pf = NULL;

	return 0;
}

我们仅仅看这个strerror库函数即可现在告诉你这个文件是没有的所以当我打开一个不存在的文档错误码就会被保存在errno变量中然后用strerror函数进行解析错误码最后用printf函数打印错误信息所以说以后运用的时候就可以这样用了。

三perror

有个perror库函数更加强大在报错领域中自行打印我们先来看一下这个库函数的介绍
在这里插入图片描述
如下代码我们将printf换成perror库函数因为perror是直接打印错误信息在打印错误信息前会先打印自定义的信息也就是说perror相当于printf+strerror

#include<stdio.h>
int main() {
	//打开文件
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL) {
		//printf("%s\n",strerror(errno));
		perror("fopen");
		return 1;
	}
	//读文件
	//关闭文件
	fclose(pf);//释放
	pf = NULL;

	return 0;
}

对于以上代码perror(“error”)是perror函数的写法所以perror会输出传参信息+冒号+错误信息。


十一字符分类函数头文件<ctype.h>

在这里插入图片描述
我们举个大小写的例子吧先来看一下islower字符分类函数的简单介绍
在这里插入图片描述

那我们试一试吧

在这里插入图片描述


十二、字符转换

int tolower ( int c );大写字母转小写
int toupper ( int c );小写字母转大写

如下代码

#include<stdio.h>
#include <ctype.h>
int main(){
	int i = 0;
	char str[] = "Test String.\n";
	char c;
	while (str[i]){
		c = str[i];
		if (isupper(c))
			c = tolower(c);
		putchar(c);
		i++;
	}
	return 0;
}

总结

这十二个是字符串函数和字符分类函数的汇总但真正的汇总可远不止这些当然库里面还有很多其他的奇特的库函数和有用的库函数在这里都没有表示出来这里只是展现了我们常用的几个库函数所以我们在以后的学习中多多运用互联网去搜寻这些库函数并进行学习当我们有一天变的很强大都掌握了的时候那就是真正学会了一门语言。


客官码字不易来个三连支持一下吧关注我不迷路

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6