目录:
本文将对字符函数和字符串函数相关的内容进行详细的讲解、使用和模拟实现,以便大家能对字符和字符串有更好的认识以及未来对字符和字符串进行操作时有更方便的方式。在编程的过程中,我们经常要处理字符和字符串,为了方便操作字符和字符串,C语⾔标准库中提供了一系列库函数,接下来我们就学习一下这些函数。
正文开始
一、字符分类函数
C语⾔中有一系列的函数是专⻔做字符分类的。这些函数的使用都需要包含一个头文件ctype.h。
这些函数的使用方法非常类似,我们只讲解其中一个函数的使用方法,其他函数都以此类推:
int islower(int c);
islower()是能够判断参数部分的c是否是小写字母的,通过返回值来说明是否是小写字母。如果是小写字母就返回非0的整数;如果不是小写字母则返回0。如果我们要写一个程序将字符串中的小写字母转为对应的大写字母,我们就可以将这个函数作为判断条件的标准然后进行转换,代码非常容易写出来,这里就直接将代码放出来:
#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 (islower(c)) c -= 32; putchar(c); i++; } return 0; }
二、字符转换函数
C语言提供了两个字符转换函数:
int tolower(int c);//将传进去的大写字母转为小写字母
int toupper(int c);//将传进去的小写字母转为大写字母
在上面的代码,我们将小写字母转为大写字母是利用ascii码中对应的大写字母和小写字母相差32来完成的,但是有了转换函数后,我们可以直接使用转换函数来实现大小字母的相互转换。
#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 (islower(c)) c = toupper(c); putchar(c); i++; } return 0; }
三、strlen()函数的使用和模拟实现
1.strlen()函数的使用
strlen()函数的原型如下:
size_t strlen(const char* str);
strlen()函数是用来计算字符串的长度的,下面是使用strlen()函数时需要注意的事项:
字符串以'\0'作为结束标志,strlen函数返回的是在字符串'\0'前⾯出现的字符个数(不包 含'\0')。
参数指向的字符串必须要以'\0'结束。
注意函数的返回值为size_t,是无符号的。
strlen的使用需要包含头文件。
#include <stdio.h> #include <string.h> 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; }
2.strlen()函数的模拟实现
模拟实现strlen()函数的方式有三种:计数器方式、不创建临时变量、指针-指针。我们依次来进行讲解。
(1)计数器方式
这个方式是依据strlen()函数计算字符串的字符个数的表述来实现的,即返回的是在字符串'\0'前⾯出现的字符个数。那么依据这个表述,我们可以创建出一个计数器,然后str指针遍历整个字符串,直到遍历到字符'\0'就停止。由ascii码我们可以直到,字符'\0'转化为整型就是数字0。那么我们就可以这样写:
//计数器⽅式 int my_strlen(const char* str) { int count = 0; assert(str); while(*str) { count++; str++; } return count; }
这里我们采用while循环,直到遍历到结尾时,*str一直都为真,所以while循环就一直运行,每执行一次循环,计数器就会+1,str指针也会向后移动一位,直到遇到字符'\0',然后再返回count,也就得到了字符串的长度。
这里再加个断言可以防止传入空字符串。
(2)不创建临时变量
如果有一道面试题是不让我们创建临时变量来模拟实现strlen()函数我们该怎么做呢?其实方法也是非常简单的。这时我们需要用到递归的思想。既然不让我们创建临时变量来统计字符个数,我们就需要在函数传参的地方动点手脚。str代表的是首元素地址,那我们在下一次进入函数的时候传入下一个字符的地址即可,这样也就起到了遍历整个字符串的作用:
//不能创建临时变量计数器 int my_strlen(const char* str) { assert(str); if(*str == '\0') return 0; else return 1+my_strlen(str+1); }
当我们首次进入my_strlen()函数时,首元素不是字符'\0',那就会进入else条件语句,其内部也就用到了函数递归。这里比较容易能想到,不再做过多赘述。
(3)指针-指针
这个方法不必多说,就是获取到最后一个元素也就是字符'\0'的地址后,再将字符'\0'的地址与首元素的地址相减即可得到字符串的字符个数。话不多说,直接上代码:
//指针-指针的⽅式 int my_strlen(char *s) { assert(str); char *p = s; while(*p != ‘\0’ ) p++; return p-s; }
四、strcpy的使用和模拟实现
1.strcpy()函数的使用
strcpy()函数是用来拷贝整个字符串的,它的原型如下:
char* strcpy(char* destination, const char* source );
其中,destination是我们要拷贝到的目标空间,source是我们要拷贝的字符串。
使用时需要注意的一些事项:
源字符串必须以'\0'结束
会将源字符串中的'\0'拷贝到目标空间
目标空间必须足够大以确保能存放源字符串
目标空间必须可修改
我们在使用strcpy()函数时,必须保证要拷贝到的字符串的大小大于等于被拷贝的字符串的大小:
int main() { char arr1[] = "abcdef"; char arr2[100] = { 0 }; printf("%s\n", strcpy(arr2, arr1)); return 0; }
2.strcpy()函数的模拟实现
字符串的拷贝,往深了讲将就是将一个字符串的字符一个个地拷贝到另一个字符串内,那么我们可以根据这个思路来模拟实现strcpy()函数。我们将需要拷贝的字符串的字符一个个拷贝到要拷贝到的字符串中,直到拷贝到字符'\0'为止,但是不光要拷贝到字符'\0'为止,我们还要将字符'\0'也一并拷贝进去,如果丢失了字符'\0',我们得到的就不再是字符串了。依据这个原理我们可以写出以下代码:
char* my_strcpy(char* dest, const char* src) { char* ret = dest; assert(dest != NULL); assert(src != NULL); while (*src!='\0') { *dest = *src; dest++; src++; } return ret; }
当src指向的位置不是字符'\0'时,那我们就可以让dest和src的对应位置进行赋值操作,随后让dest指针和src指针都向后移动一位。这样,src字符串就会被拷贝到dest字符串中。但这样的代码还是可以优化的。我们知道,在进行赋值时只要一直赋的是非0的数,条件就会一直被判定为真;一旦赋值时赋的是0,条件就会判定为假,跳出循环。而每个循环都至少需要一个语句,这时就需要用到我们的空语句,即什么操作都不执行的语句。
char *my_strcpy(char *dest, const char*src) { char *ret = dest; assert(dest != NULL); assert(src != NULL); while((*dest++ = *src++)) { ; } return ret; }
这样,拷贝操作就可以在判断条件时发生,而while循环内部不再需要进行任何操作,只需要写上一个不执行任何操作的空语句即可。当拷贝字符串到字符'\0'时,拷贝完成,也就跳出循环,返回需要拷贝的字符串。
五、strcat()函数的使用和模拟实现
1.strcat()函数的使用
strcat()函数是用来将一个字符串拼接到另一个字符串的结尾的。它的原型如下:
char * strcat(char* destination, const char* source);
其中destination是需要被拼接的字符串,source是需要拼接的字符串。当字符串拼接完成后,拼接后的字符串将覆盖destination原来的字符串。使用strcat()函数时需要注意的一些事项:
源字符串必须以'\0'结束
目标字符串中也得有'\0',否则没办法知道追加从哪里开始
目标空间必须有⾜够的大,能容纳下源字符串的内容
目标空间必须可修改
int main() { char arr1[10] = "abcdef"; char arr2[] = "xyz"; printf("%s\n", strcat(arr1, arr2)); return 0; }
2.strcat()函数的模拟实现
我们往深了分析strcat()函数的运作时,实际上就是将destination中结尾的字符'\0'拿source中的首元素覆盖住,然后再遍历source中的每个字符追加到destination后,完成字符串的拼接。也就是说,我们需要先让destination的指针指向destination字符串中的'\0',然后再将source字符串拷贝进去即可:
char *my_strcat(char *dest, const char*src) { char *ret = dest; assert(dest != NULL); assert(src != NULL); while(*dest) { dest++; } while((*dest++ = *src++)) { ; } return ret; }
六、strcmp()函数的使用和模拟实现
1.strcmp()函数的使用
strcmp()函数是用来比较两个字符串的大小的,它的原型如下:
int strcmp(const char* str1, const char* str2);
2.strcmp()函数的模拟实现
我们在C语言:指针详解(4)-CSDN博客中已经讲到过strcmp()是如何比较两个字符串的大小了,也就是相同部分不用比较,只需要比较不同部分的字符的ASCII码值的大小即可,同时也要注意两个字符串长短的差异,这里不再过多的赘述了。
在比较字符串的大小时,两个字符串总是会有一定的长度差异和字符差异,但往往都是两个字符串的第一个互不相同的字符的ascii码值起到了决定性作用,我们可以依据这个原理来模拟实现strcmp()函数。我们只需要遍历完两个字符串相同的部分,直到遇到第一个互不相同的字符进行比较即可,如果长度相同组成字符也相同,这时两个字符串就会直接遍历到字符'\0',那么这两个字符串就相等。代码还是很好想的:
int my_strcmp(const char* str1, const char* str2) { int ret = 0 ; assert(src != NULL); assert(dest != NULL); while(*str1 == *str2) { if(*str1 == '\0') return 0; str1++; str2++; } return *str1-*str2; }
七、strn-系列
接下来要讲的strn系列的函数分别包括strncpy、strncat、strncmp,可以很清楚地发现这三个函数与上面的函数都差了一个n,这是什么意思呢?接下来我们一起来探讨它们该如何使用。这里不再讲述如何模拟实现,模拟实现与无n系列类似,如果需要自己进行模拟实现,可以找出空余时间进行模拟实现。
1.strncpy()函数
strncpy()函数与strcpy()函数的作用是一样的,都是起到拷贝字符串的作用。唯一不同的一点就是strncpy既可以全部拷贝,也可以进行局部拷贝,即指定起始位置进行拷贝。它的原型如下:
char* strncat(char* destination, const char* source, size_t num);
destination是要拷贝到的字符串,source是要被拷贝的字符串,num是相对source首元素的偏移值,即你要从source字符串拷贝的字符的个数。如果source字符串的字符个数小于n,则将source中的字符拷贝到destination中后,剩余的个数将由字符'\0'填满;如果等于,则就是起到了strcpy()函数的作用,即拷贝整个字符串;如果大于,则按个数来进行拷贝。
2.strncat()函数
strncat()函数同样也是与strcat()函数的作用一样,它的原型如下:
char* strncat(char* destination, const char* source, size_t num);
这个函数的作用是将source指向的字符串追加到destination指向的字符串的末尾,最多追加num个字符。如果source字符串的长度小于n,则destination的剩余部分将被空字符('\0')填充。如果source字符串的长度等于或大于n,则destination将追加source字符串的前n个字符,并在这些字符后面加上空字符以确保字符串正确终止。
3.strncmp()函数
strncmp()函数与strcmp()函数的作用的一样的,它的原型如下:
int strncmp(const char* str1, const char* str2, size_t num);
这个函数比较str1和str2指向的两个字符串的前num个字符。如果在这num个字符中找到第一个不同的字符对,则strncmp返回这个字符的ASCII值的差。如果所有前num个字符都相同,或者其中一个字符串的长度少于num个字符(以空字符'\0'结束),则strncmp返回0。
八、strstr()函数的使用和模拟实现
1.strstr()函数的使用
strstr()函数是用来进行字符串搜索的,它的原型如下:
char* strstr(const char* str1, const char* str2);
其中str1是要进行搜索的字符串,str2是要进行搜索的内容字符串。如果str1中含有str2,会返回str2在str1所在的位置的指针;如果str1中不含str2,则返回空指针(NULL)。
int main() { char arr1[] = "abcdef"; char arr2[] = "xyz"; int flag = strstr(arr1, arr2); if (flag > 0) { printf("arr1中存在arr2\n"); } else { printf("arr1中不存在arr2\n"); } return 0; }
2.strstr()函数的模拟实现
接下来我们来进行对strstr()函数的模拟实现。首先我们要明白的是我们是要在str1中查找str2。如果str2为空字符串,也就是说它内部仅仅只有字符'\0',这时我们说str1实际上是包含str2的,因为str1也是字符串,其内部也含有字符'\0',这时我们只需返回str1的首元素指针即可,这是极为特殊的情况,那么剩下的都是普遍情况。
紧接着,如果我们需要连续比较两个字符串的内容,有个前提条件就是保证两个字符串的指针所指向的内容不为空且其字符必须相等,只有这样才能达到搜索的效果。同时我们需要再额外定义两个指针来存储str1的首元素地址,让其中一个来代替str1进行遍历,另一个则是在遍历的过程中记录下str2在str1第一次出现的位置的指针。有了思路之后我们就可以写出如下代码:
char* strstr(const char* str1, const char* str2) { char* cp = (char*)str1; char* s1, *s2; if (!*str2) return (char* )str1; while (*cp) { s1 = cp; s2 = (char*)str2; while((*s1 && *s2 && !(*s1-*s2)) s1++, s2++; if (!*s2) return cp; cp++; } return NULL; }
九、strtok()函数的使用
strtok()函数的作用是用来分割字符串的,它的原型如下:
char* strtok(char* str, const char* sep);
这个函数接受两个参数:str是一个指向字符串的指针,表示要被分割的原始字符串;sep是一个字符串,包含了一系列用作分隔符的字符。strtok()函数会在str中查找第一个出现sep中任一字符的位置,并将该字符替换为字符串结束标志'\0',然后返回指向分割出的第一个token的指针。后续的调用strtok时,第一个参数应该设置为NULL,以便函数继续在原始字符串中寻找下一个token。
#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; }
十、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() { int i = 0; for (i = 0; i <= 10; i++) { printf("%s\n", strerror(i)); } return 0; }
在Windows11+VS2022环境下输出的结果如下:
举例:
#include <stdio.h> #include <string.h> #include <errno.h> int main () { FILE* pFile; pFile = fopen ("unexist.ent","r"); if(pFile == NULL) printf ("Error opening file unexist.ent: %s\n", strerror(errno)); return 0; }
输出:
也可以了解⼀下perror函数,perror函数相当于一次将上述代码中的第9行完成了,直接将错误信息打印出来。perror函数打印完参数部分的字符串后,打印一个冒号和一个空格再打印错误信息。这里会在后续文件操作的内容详细讲解。
以上函数的使用需要包含string.h头文件
完