一.字符分类函数
二.字符转换函数
三.c语言中str库函数的使用及模拟实现
1.字符分类函数
在c语言中,有一系列函数是专门做字符分类的,也就是说,判断一个字符属于什么字符。他们都需要包含一个对应的头文件----->ctype.h 。
为了能更清楚的知道这些函数的参数以及返回值,我们不妨去www.cplusplus.com这个网站去查阅,切换到旧的版本下面,我们其中的islower为例:
,这里有两个点我们需要注意,一个是参数部分为什么是int类型,字符不应该是char类型吗?这其实是因为字符在c语言中本质上其实还是int,因为每一个字符在c语言中都有对应的ascll值。
第二个需要注意的是返回值,当c为小写,则返回非零值(通常返回1,具体返回什么看编译器,反正为非零值),若为大写,则返回0。其他函数也可以参考这样去了解其相关参数和返回值,以及一些具体使用事项。
既然在这里提到了islower,isupper用于判断是否为大小写字母,那么有没有一种函数可以在我判断之后,进行大小写转换?答案是有的,c语言恰恰好为我们提供了一些字符转换函数:tolower,toupper。
同样的,我们打开上面的网站,查询我们想知道的函数,
,在这里我们需要注意一个点:第一个就是返回值为什么是int,返回的难道不是char字符吗?这里的解释跟上面islower解释类似,函数内部返回一个ascll码值,用putchar转换成对应的字符即可。
说归说,做归做,咱们1写一段代码,来小试牛刀,进行大小写转换:
#include<stdio.h>
#include<ctype.h>
int main()
{
char arr[] = "Hello World";
char* p = arr;
int sz = sizeof(arr) / sizeof(arr[0]);
char* pa = p + sz - 1;
int i = 0;
while (p<=pa)
{
if (islower(*p))
{
int ret = toupper(*p);
arr[i] = putchar(ret);
}
else
{
int ret = tolower(*p);
arr[i] = putchar(ret);
}
i++;
p++;
}
return 0;
}
,这样便实现了字母的大小写转换。
3.c语言中str库函数的使用及模拟实现
1.strlen的使用以及模拟实现
,返回值是size_t类型的无符号整数,参数部分是一个char类型的指针,且指向的内容用const修饰,不可被修改。strlen在统计字符个数时,统计到‘\0’之前,所以字符串里面需要有‘\0'作为结束标志,但是’\0‘strlen不会统计进去。
,那么,我们在进行监视一下一下arr这个数组:
,我们发现,arr数组末尾是放了一个’\0'的。
了解完strlen的相关用法之后,那么现在我们试着来模拟实现一下:
方法一:通过计数器的方式
int my_strlen(const char* str)
{
int count = 0;
assert(str) //assert断言判断str不为空指针
while(*str) //'\0'的ascll码值为0
{
count++;
str++;
}
return count;
}
第二种方法:用递归的方式
int my_strlen(const char* str)
{
if(*str=='\0')
return 0;
else
return 1+my_strlen(str+1)
return count;
}
,具体递归的逻辑可以参考了解我之前的博客关于用函数递归解决汉诺塔问题,在这篇博客里面,有提到过画图理解递归运行的逻辑。
第三种方法:指针模拟实现strlen
其实这个就是指针减指针的方式:
int my_strlen(char* p)
{
char *p1 = p; //记录最开始的位置
aseert(p) //断言判断p不为NULL
while(p!='\0')
{
p++;
}
return p - p1;//指针相减为元数个数
}
。
2.strcpy的使用以及模拟实现
,首先参数部分是两个指针,一个源头(source)一个destination(目的地),而返回值是一个char*的地址。在使用strcpy的时候,我们需要明白一下几个注意的点:
,首先我们看第二点,为了验证‘\0’是否被一起拷贝到目标数组里,我们不妨用以下代码来进行验证:
#include<stdio.h>
#include<string.h>
int main()
{
char arr1[10] = "xxxxxxxx";
char arr2[] = "world";
strcpy(arr1, arr2);
printf("%s",arr1);
return 0;
}
,我们监视数组,观察arr1前后数组内容的变化,可以不难发现arr2数组后的'\0'也被拷贝进arr1数组:
。那么在了解完其基本要点之后我们不妨来试试自己模拟实现:
char* my_strcpy(char* dest,char* src)
{
char* ret = dest //当后续交换赋值时,两个指针向后走,不在原来位置,所以需要记录目标数组起始地址
aseert(dest!=NULL)
aseertsrc!=NULL)
while(*dest++=*src++)
{
;
}
return ret; //传回目标地址
}
,其中最巧妙的代码是这句:
while(*dest++=*src++)
{
;
}
将dest与src交换作为while循环的条件,当src不为空,赋值给dest完成第一个元素的拷贝,指针向后+1,循环继续,直到src为空,ascll码为0,循环停止,而这个while循环内部为空语句所以只会判断们不会执行。
3.strcat的使用和模拟实现
同样的,我们先对这个函数,有一定的了解
,大致与上面的strcpy类似,下面我们依旧先来了解了解这个函数的基本注意要点:
那么问题来了,原字符串追加完成是否会保留'\0'?以及目标字符原'\0'是否会被替代?
下面我们依旧以一段代码进行说明:
,当我们把数组长度恰好设为arr1+arr2的长度,但是程序拼接时却崩了,这是因为拼接会把原数组结尾'\0'一起拼接过去,而我们刚刚数组长度不够,所以会出现如上情况。
了解了相关注意要点,我们不妨来试试自己进行模拟实现strcat:
char *my_strcat(char *dest, const char*src)
{
char *ret = dest;
assert(dest != NULL);
assert(src != NULL);
while(*dest) //循环遍历目标数组,直到遇到结尾'\0'
{
dest++;
}
while((*dest++ = *src++)) //重复拷贝覆盖逻辑,参考模拟实现strcpy
{
;
}
return ret;
}
。
3.strcmp的使⽤和模拟实现
通过上面,我们可以总结以下几个注意要点:
◦ 第⼀个字符串⼤于第⼆个字符串,则返回⼤于0的数字
◦ 第⼀个字符串等于第⼆个字符串,则返回0
◦ 第⼀个字符串⼩于第⼆个字符串,则返回⼩于0的数字
而两两之间进行比较依据依旧是ascll码表。下面我们就来进行模拟实现:
int my_strcmp(const char*str1,const char* str2)
{
assert(*str1!=NULL);
assert(*str2!=NULL);
while(*str1==*str2) //循环的条件是两个字符串当前指针所指向的地址的内容的字符相等
{
if(*str1=='\0') //当有一个字符串遇到了末尾'\0',则说明两个字符串相等
return 0;
str1++; //比完一个,指针位置+1,比较下一个字符
str2++; //比完一个,指针位置+1,比较下一个字符
}
return *str1 - *str2;
}
。
4.strn系列库函数
strn系列函数比str函数多了n,这个n表示个数,就是我们可以控制想要比较,拷贝,拼接字符串内字符的个数,以strcpy为例:
,我们发现参数部分多了一个size_t类型的无符号整数,num表示我们需要进行操作的字符串当中字符的个数,相关模拟实现我们可以用到循环,每次执行一次,num--一次,直到循环结束,num恰好为0,这里的模拟实现就不过多赘述。
5.strstr的使用和模拟实现
str为字符串,那么strstr则为查找字符串
,在这里我们可以知道strstr的基本使用方法,比如想要在字符串arr1里面查找字符串arr2,如果找到了则返回在arr2在arr1里面第一次出现的指针,如果没有找到则返回空指针(NULL),为了更加直观的看到如何使用,我们用下面的这段代码为例:
。
在了解完简单的基本使用后,我们现在不妨试试去模拟实现:
在模拟实现时,我们主要会遇到三种情况,我们这样概括:
1.“abcdef”和“bcd”
2."abbbcdef"和“bbc”
3.“abcdef”和“bbq”
首先我们在尝试模拟实现时,如果遇到了第一种情况,第一个字符a不相等,第二个字符往后bcd相等,我们需要返回第一个字符串当中b的地址,那么在模拟实现时,我们需要两个指针分别指向两个字符串第一个字符串首元素地址,比较一个指针向后+1,直到查找完全部字符,即当被查找字符串遇到了'\0',我们就可以认为找到了。
当第二种情况较为复杂,当我们比较一个字符,不相等,第一个字符串指针向后+1,第二个字符串不变,当第一个字符串指针来到第二个字符时,判断相等,两个字符串指针分别向后+1,当比到第一个字符串第四个字符时,发现不相等,此时,我们应当让让第二个字符串指针回到起始位置,第一个字符串指针回到当前比较的位置,下一次比较在当前比较位置指针+1处开始,这里我们其实不难发现。貌似两个指针不太够,我们需要多个指针记录位置。
第三种情况,逻辑上较为简单,当查找完全部字符串,还未找到,则返回空指针。
那么有没有一种代码,可以同时满足以上三种情况呢?答案毋庸置疑,下面,我们来试着一起模拟实现一下:
char* my_strstr(const char* str1,const char*str2)
{
char* cur = str1;
char* s1 = str1;
char* s2 = str2;
if(*str2=='\0') //如果str2为NULL,则返回第一个字符串首元素地址
return (char*)str1;
while(*cur)
{
s1 = cur;
s2 = str2;
while(*s1==*s2)
{
s1++;
s2++;
}
if(s2 == '0')
return cur
cur++;
}
return NULL;
而这一段代码恰好满足了前三情况,逻辑主要是第二种情况,当解决第二种情况,其他情况也迎刃而解。
6.strtok函数的使用(字符分割函数,可用于将一个字符串分割成不同字符串)
下面就是一些这个函数的注意要点
基本型为:strtokk(arr,sep)
arr(字符串首元素地址),sep(分隔符标志)
• sep参数指向⼀个字符串,定义了⽤作分隔符的字符集合
• 第⼀个参数指定⼀个字符串,它包含了0个或者多个由sep字符串中⼀个或者多个分隔符分割的标
记。
• strtok函数找到str中的下⼀个标记,并将其⽤ \0 结尾,返回⼀个指向这个标记的指针。(注:
strtok函数会改变被操作的字符串,所以在使⽤strtok函数切分的字符串⼀般都是临时拷⻉的内容
并且可修改。)
• strtok函数的第⼀个参数不为 NULL ,函数将找到str中第⼀个标记,strtok函数将保存它在字符串
中的位置。
• strtok函数的第⼀个参数为 NULL ,函数将在同⼀个字符串中被保存的位置开始,查找下⼀个标
记。
• 如果字符串中不存在更多的标记,则返回 NULL 指针
下面我们用一段代码来具体感悟:
#include <stdio.h>
#include <string.h>
int main()
{
char arr[] = "192.168.6.111";
char* sep = ".";
char* str = NULL;
for (str = strtok(arr, sep); str != NULL; str = strtok(NULL, sep))
{
printf("%s\n", str);
}
return 0;
}
这段代码巧妙的是这个for循环,因为我们分割字符串是多次查找,所以是循环,而循环的判断根据strtok函数性质来写:
• strtok函数的第⼀个参数不为 NULL ,函数将找到str中第⼀个标记,strtok函数将保存它在字符串
中的位置。
• strtok函数的第⼀个参数为 NULL ,函数将在同⼀个字符串中被保存的位置开始,查找下⼀个标
记。
这个strtok模拟实现较为复杂,为了保证模拟实现时,函数栈帧不会被销毁,应该遇到了类似于static一类的静态变量来保存我指针位置,实现较为复杂,这里不多赘述。
7.strerror与perror
这两个函数相对上面函数则更好理解,主要是对错误信息的打印。
strerror返回值为地址,这个数字包含错误信息地址,用一个指针变量接受并打印就可以看到程序错误的原因:strerror(errno),errno为全面变量用来存储错误码,错误码对应的错误信息保存在errno这个头文件中。
而perrpr与strerror基本上差不多,只不过多了一个打印功能,自动在打印的信息里+上了冒号和空格。
关于这篇博客就到这里啦
谢谢大家观看~