目录
一、字符分类函数
二.字符转换函数
三.字符串函数
一、字符分类函数
C语言中有一系列的函数是专门做字符分类的,也就是一个字符是属于什么类型的字符的,这些函数的使用都需要包括一个头文件是 ctype.h
isalnum 检查字符是否为字母数字(函数)
isalpha 检查字符是否是字母表(函数)
isblank 检查字符是否为空(函数)
iscntrl 检查字符是否为控件字符(功能)
isdigit 检查字符是否为十进制数字(函数)
isgraph 检查字符是否具有图形表示(功能)
islower 检查字符是否为小写字母(函数)
isprint 检查字符是否可打印(功能)
ispunct 检查字符是否为标点符号(功能)
isspace 检查字符是否为空白(函数)
isupper 检查字符是否为大写字母(函数)
isxdigit 检查字符是否为十六进制数字(函数)
islower
int islower ( int c );
我们举例islower来说明,检查字符是否为小写字母,字符的本质和整型一样是ASCII码值
那返回值是int是什么意思呢
这句话的意思是如果c是一个小写字符的话就返回非0的数字,不是的话则返回0
#include <stdio.h>
#include <ctype.h>
int main()
{int ret = islower('A');printf("%d\n", ret);ret = islower('a');printf("%d\n", ret);ret = islower('0');printf("%d\n", ret);return 0;
}
0也不是小写字母
我们再举例isdigit isdigit是判断十进制数字‘0’~‘9’的字符
#include <stdio.h>
#include <ctype.h>
int main()
{int ret = isdigit('A');printf("%d\n", ret);return 0;
}
很显然不是十进制的数字字符,所以返回值为0
那么是十六进制的字符吗,这里我们用到isxdigit
#include <stdio.h>
#include <ctype.h>
int main()
{int ret = isxdigit('A');printf("%d\n", ret);return 0;
}
返回值为128是十六进制的字符
我们也可以利用这些字符函数写一个代码,将字符串中的小写字母转大写,其他字符不变
#include <stdio.h>
#include <ctype.h>
#include <string.h>
int main()
{char arr[] = "I am a Student.";int i = 0;while (arr[i] != '\0'){if (islower(arr[i]))arr[i]=arr[i]-32;i++;}printf("%s", arr);return 0;
}
二.字符转换函数
c语言中提供了两个字符转换函数
int toupper(int c);//将参数传进去的小写字母转大写
int tolower(int c);//将参数传进去的大写字母转小写
int main()
{char ch = toupper('a');printf("%c", ch);return 0;
}
本身是小写会转换成大写,那么本身是大写那么将不做任何的转换
同理说大写转小写也是同样的操作
那么我们将上面的代码按照这种逻辑来进行转换,然而toupper在字符处理中只会处理单个字符,所以我们在使用的时候需要一个一个的进行转换。
#include <stdio.h>
#include <ctype.h>
#include <string.h>
int main()
{char arr[] = "I am a Student.";int i = 0;while (arr[i] != '\0'){if (islower(arr[i]))arr[i]=toupper(arr[i]);i++;}printf("%s", arr);return 0;
}
三.字符串函数
字符串相关的函数所用到的头文件都是string.h
1.strlen的使用和模拟实现
size_t strlen(const char* str);
字符串以'\0'作为结束标志,strlen函数返回的是字符串中'\0'前面出现的字符个数(不包括'\0')
#include <string.h>
int main()
{char arr[] = "abcdef";size_t len = strlen(arr);printf("%d", len);
}
假如我们不给予它'\0'的地址
int main()
{char arr[] = { 'a','b','c' };size_t len = strlen(arr);printf("%zd", len);return 0;
}
那么运行结果将会出现随机值
注意函数的返回值是size_t,是无符号的
int main()
{if (strlen("abc") - strlen("abcdef") > 0){printf(">\n");}else{printf("<=");}return 0;
}
虽然数据上得到的结果是-3,但是实际上无符号减去无符号其实也是无符号整数
-3的补码是11111111111111111111111111111101 开头的1原本是符号位但是我们无符号的话就让他变成了一个非常大的整数
那么我们如果想让他变成-3来判断就需要用到强制转换
int main()
{if ((int)strlen("abc") - (int)strlen("abcdef") > 0){printf(">\n");}else{printf("<=");}return 0;
}
还可以用这种写法进行判断
int main()
{if (strlen("abc") > strlen("abcdef")){printf(">\n");}else{printf("<=");}return 0;
}
模拟实现strlen函数
第一种方法指针-指针的方式
第二种方法计数器的方式
#include <assert.h>
size_t my_strlen(const char * str)
{int count = 0;assert(str != NULL);while (*str){count++;str++;}return count;
}
int main()
{char arr[] = "abcdef";size_t len = my_strlen(arr);printf("%zd", len);return 0;
}
第三种方法是递归的实现方式
size_t my_strlen(const char* str)
{assert(str != NULL);if (*str){return 1 + my_strlen(str + 1);}else{return 0;}
}
int main()
{char arr[] = "abcdef";size_t len = my_strlen(arr);printf("%zd", len);return 0;
}
我们会发现my_strlen("abcdef")可以拆解为1+my_strlen("bcdef")等等以此类推用递归的方法来实现计算出字符串的长度,这样我们就不用通过创建变量来实现这个函数的模拟实现
2.strcpy的模拟实现
char * strcpy ( char * destination, const char * source );//前一个是目的地后一个是源头
这是一个字符串拷贝的函数,将会拷贝字符串包括结束的'\0’的字符
int main()
{char arr1[] = "abcdef";char arr2[20] = { 0 };strcpy(arr2, arr1);printf("%s", arr2);
}
int main()
{char arr1[] = "abcdef";char arr2[20] = "xxxxxxxxxx";strcpy(arr2, arr1);printf("%s", arr2);
}
通过这两个代码可以发现结束的时候会带上'\0’
注意事项
源头的字符串中必须包含'\0’,不然不知道上面时候结束
其此目标的空间需要足够大
还有就是目标空间必须可修改
模拟实现一个strcpy
my_strcpy(char* dest, const char* str)
{assert(dest && str);char* ret = dest;while (*str != '\0'){*dest = *str;str++;dest++;}*dest = *str;//将最后的'\0'给它拷贝过去return ret;
}
int main()
{char arr1[] = "abcdef";char arr2[20] = { 0 };my_strcpy(arr2, arr1);printf("%s", arr2);return 0;
}
返回的是目标空间的起始地址
我们还可以接着优化这段代码
my_strcpy(char* dest, const char* str)
{assert(dest && str);char* ret = dest;while (*dest++ = *str++){;}*dest = *str;//将最后的'\0'给它拷贝过去return ret;
}
++是延后产生的,拷贝过去字符后,判断表达式的值,当0拷贝过去后,循环就停止
这样写我们的代码就很具有灵动性
3.strcat的模拟实现
char * strcat ( char * destination, const char * source );
这是一串字符串追加的函数
int main()
{char arr1[20] = "hello ";char arr2[] = "world";strcat(arr1, arr2);printf("%s", arr1);
}
那我们是如何追加的呢
1..找到目标空间的末尾\0
2.再将源字符串拷贝过来
目标空间要足够大
strcat的模拟实现
char* my_strcat(char* dest, const char* str)
{assert(dest && str);char* len = dest;while (*dest)dest++;while (*dest++ = *str++){;}return len;
}
int main()
{char arr1[20] = "hello ";char arr2[] = "world";my_strcat(arr1, arr2);printf("%s", arr1);
}
这里我们补充一下
0——数字0
‘0’——数字字符0——ASCII值是48
‘\0’——\ddd ddd表示1-3给8进制数字
\0——ASCII值是0
NULL---0
警告的是这里我们最好不要将字符串自己给自己追加否则会陷入死循环
4.strcmp函数的使用和模拟实现
int strcmp ( const char * str1, const char * str2 );
strcmp是用来比较字符串的
strcmp函数是比较的对应ASCII值的大小,q大于d所以说下面那个就大于上面那个,不会说你字符串而比较长而影响到我
int main()
{char arr1[] = "abcdef";char arr2[] = "abq";int let=strcmp(arr1, arr2);printf("%d", let);
}
返回值是-1符合我们前面的分析
strcmp的模拟实现
int my_strcmp(const char*str1, const char*str2)
{assert(str1 && str2);while (*str1 ==* str2){if (*str1 == '\0'){return 0;}else{str1++;str2++;}}return *str1 - *str2;
}
int main()
{char arr1[] = "abcdef";char arr2[] = "abq";int let=my_strcmp(arr1, arr2);printf("%d", let);
}
5.strncpy的使用
char * strncpy ( char * destination, const char * source, size_t num );
strncpy strncat strncmp与上面不同的是这些都是长度受限制的字符串函数
int main()
{char arr1[20] = "abcdef";char arr2[20] = { 0 };strncpy(arr2, arr1,3);printf("%s", arr2);
}
这样我们就将abc拷贝过去了但是我们不会带过去\0
int main()
{char arr1[20] = "abcdef";char arr2[20] = "xxxxxxxxxxx";strncpy(arr2, arr1,8);printf("%s", arr2);
}
拷贝位置不足的时候我们会补\0
具体的实现方法与上面大差不差,大家可以自己去模拟实现一下
6.strncat的使用
char * strncat ( char * destination, const char * source, size_t num );
int main()
{char arr1[20] = "abcdef\0yyyyyyy";char arr2[20] = "xxxxxxxxxxx";strncat(arr1, arr2,3);printf("%s", arr2);
}
从这里我们不难看出其是从\0的位置处开始的追加
7.strncmp的使用
int strncmp ( const char * str1, const char * str2, size_t num );
int main()
{char arr1[20] = "abcdef";char arr2[20] = "ab";int ret=strncmp(arr1, arr2,3);printf("%d", ret);
}
8.strtok函数的使用
char * strtok ( char * str, const char * sep );
sep参数指向一个字符串,定义了用作分隔符的字符集合
第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标记
strtok函数找到str中的下一个标记,并将其用\结尾,返回一个指向这个标记的指针
strtok函数会改变被操作的字符串,所以strtok函数切分的字符串一般都是临时拷贝的内容并且可以修改
strtok函数的第一个参数不为NULL,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置。就是标记完\0以后,下一次会从\0的这个位置开始找
strtok的第一个参数为NULL函数将在同一个字符串中被保存的位置开始,查找下一个标记
如果字符串中不存在更多的标记,则返回NULL指针
int main()
{char arr[] = "liuli@yeah.net";//liuli/0yeah/0netchar buf[256] = { 0 };strcpy(buf, arr);char* sep = "@.";char* ret = strtok(buf, sep);printf("%s", ret);return 0;
}
int main()
{char arr[] = "liuli@yeah.net";char buf[256] = { 0 };strcpy(buf, arr);char* sep = "@.";char* ret = strtok(buf, sep);printf("%s\n", ret);ret = strtok(NULL, sep);printf("%s\n", ret);ret = strtok(NULL, sep);printf("%s\n", ret);return 0;
}
再往后找就只能找到空指针,并且将它打印出来
这是我们知道将它且几份的前提下才完成这样的操作,那么我们能否给它完成通用的形式呢
int main()
{char arr[] = "liuli@yeah.net";char buf[256] = { 0 };strcpy(buf, arr);char* sep = "@.";char* ret = NULL;for(ret = strtok(buf, sep);ret!=NULL;ret=strtok(NULL,sep))printf("%s\n", ret);return 0;
}
第一次我们初始化进行打印然后进行判断ret不为空指针,就这样一直循环就可以完成上面的结果了
9.strstr函数的使用和模拟实现
const char * strstr ( const char * str1, const char * str2 );char * strstr ( char * str1, const char * str2 );
str1在str2是否出现过,如果出现过一次及以上,我们要的是第一次出现的起始地址,没找到则返回NULL指针
int main()
{char arr[] = "abcdefg";char buf[] = "cde";char* ret = strstr(arr, buf);printf("%s", ret);return 0;
}
由于我们找到的是第一次出现的起始位置,所以我们打印出来的结果就是cdefg
strstr的模拟实现
const char* my_strstr(const char* str1, const char* str2)
{assert(str1 && str2);const char* s1 = NULL;const char* s2 = NULL;const char* cur = str1;if (*str2 == '\0')return str1;while (*cur){s1 = cur;s2 = str2;while (*s1 &&* s2 && *s1 ==* s2){s1++;s2++;}if (*s2 == '\0')return cur;cur++;}return NULL;
}
int main()
{char arr[] = "abbbcdef";char *buf = "bbc";const char* ret = my_strstr(arr, buf);if (ret == NULL){printf("不存在");}elseprintf("%s", ret);return 0;
}
这就很好的解决了各种未知潜藏的问题
12.strerror函数的使用
char * strerror ( int errnum );
sterror函数可以把参数部分错误码对应的错误信息的字符串地址返回来
在不同的系统和c语言标准库的视线中都规定了一些错误码,一般是放在errno.h这个头文件中说明的,c语言程序启动的时候就会使用一个全局的变量errno来记录程序当前错误码,只不过程序启动的时候errno是0,表示没有错误,当我们在标准库中的函数的时候发生了某种错误,就会将对应的错误码放在errno中,而一个错误码的数字是整数很难理解是什么意思,所以每一个错误码都是有对应的错误信息的。strerror函数就可以将错误对应的错误信息字符串的地址返回。
int main()
{for (int i = 0; i <10; i++){printf("%d:%s\n", i, strerror(i));}return 0;
}
就会将其错误的信息打出来当然不止这几个那么如何用呢我们举个例子
#include <errno.h>
int main()
{FILE* pf = fopen("data.txt", "r");if (pf == NULL){printf("打开文件失败,原因是:%s\n", strerror(errno));return 1;}else{printf("打开文件成功");fclose(pf);pf = NULL;}return 0;
}
我们还有一个更方便的函数perror其实它算的上是printf+strerror
#include <errno.h>
int main()
{FILE* pf = fopen("data.txt", "r");if (pf == NULL){printf("打开文件失败,原因是:%s\n", strerror(errno));perror("打开文件失败,原因是");return 1;}else{printf("打开文件成功");fclose(pf);pf = NULL;}return 0;
}
这就是有关字符函数和字符串函数的内容了如果有可以优化的地方和不足的地方欢迎大家指出