目录
一、字符函数
1.1 字符分类函数
用于字符分类的函数(都需要头文件ctype.h),这里我用isdigit来举例,他用来判断是否为十进制数字字符,如果是则返回数字字符那就会返回非零数,而要是其他字符只返回零,格式为int isdigit ((int c)里面是整型);
代码展示:
#include<stdio.h>
#include<ctype.h>
int main()
{
int i = 0;
const char* str = "abcd123";
while (str[i])
{
if (isdigit(str[i]))
{
printf("%d ", str[i]-48);//打印字符数字
}
str++;
}
}
再举一个islower判断小写字符,格式和isdigit是一样的直接代码展示
#include<stdio.h>
#include<ctype.h>
int main()
{
int i = 0;
const char* str = "abcTHCefg";
while (str[i])
{
if (islower(str[i]))
{
printf("%c ",str[i]);
}
str++;
}
return 0;
}
其他字符分类函数格式也是一样的,想使用哪个就可以使用,都是用来判断字符是否为数字、大写、小写这些,判断什么主要看后面英文单词。需要注意的是判断的是整型(不能直接写成字符,只能通过字符转换为ascll值才能使用,一般解析时会解析为ascll值)
1.2 字符转换函数
字符转换函数c语言种只有2种一种是tolower、另一种就是toupper,这两个格式是一样的,头文件也需要ctype.h,格式为(int)tolower((int)同样的是整型)
代码展示:
#include<stdio.h>
#include<ctype.h>
int main()
{
const char* str = "aThJbtuiKHGJop";
int i = 0;
while (str[i])
{
printf("%c", toupper(str[i]));
i++;
}
return 0;
}
二、字符串函数
2.1 strcpy
2.1.1 strcpy库函数的使用
strcpy这个库函数用来拷贝字符串,需要头文件string.h,格式为strcpy(拷贝地址(指针),字符 串(变量也可以)),他会识别\0这个停止符号,拷贝地址大小也要包含\0这个大小,内部被拷贝的字符串不能为空,否则会报错。(使用前提拷贝地址大小一定要大于被拷贝的地址,并且必须拷贝地址是可以修改的地址)
代码:
#include<stdio.h>
#include<string.h>
int main()
{
char str1[20];
const char* str2 = "abcdef";
strcpy(str1,str2);
printf("%s\n", str1);
return 0;
}
2.1.2 strcpy库函数的模拟实现
strcpy的原理很简单就是将被拷贝内元素一个一个的转移到拷贝地址中,并将内部\0最后字符也拷贝过来,直接代码展示:
#include<stdio.h>
char* my_strcpy(char* dest,char* src)
{
char* ret = src;//拷贝地址
while (*src != '\0')//条件
{
*dest = *src;
src++;
dest++;
}
*dest = *src;//拷贝\0
return ret;
}
int main()
{
char str1[20];//拷贝
char* str2 = "abcdef";//被拷贝
char* ret = my_strcpy(str1, str2);
printf("%s\n", ret);
return 0;
}
2.2 strcat
2.2.1 strcat库函数的使用
strcat这个库函数是用来拼接字符或字符串的,需要头文件string.h,格式为strcat(拼接的地址(指针),字符串(该字符串拼接到前面那个指针地址后面))(前提是拼接的地址是可以修改的,并且两边都需要有\0,空间也必须大)
#include<stdio.h>
#include<string.h>
int main()
{
char str1[20] = "abcdefgh";//拼接数组
const char* str2 = "abcde";//被拼接数组
strcat(str1, str2);
printf("%s\n", str1);
return 0;
}
2.2.2 strcat函数的模拟实现
strcat库函数的底层就是将拼接的地址移动到\0开始拼接(包括\0),strcat(str1,str2)然后就是不断改变str1,代码展示:
#include<stdio.h>
char* my_strcat(char* dest, char* src)
{
char* ret = dest;//拷贝起始地址
while (*dest != '\0')//移动到\0
{
dest++;
}
while (*src != '\0')//拼接
{
*dest = *src;
dest++;
src++;
}
*dest = *src;
return ret;
}
int main()
{
char str1[20] = "abcde";//拼接到这
char* str2 = "fghyz";//拼接过去
char* ret = my_strcat(str1, str2);
printf("%s\n", str1);
return 0;
}
2.3 strcmp模拟
首先我们要知道strcmp的底层原理,strcmp是用于比较字符串大小的,它是通过对比两个字符串中一个一个字符进行对比,实际就是对比ascll值,然后返回值就是大于0、小于0或者等于0,我们知道这个原理后就可以开始模拟使用这个函数了。
#include<stdio.h>
#include<assert.h>
int my_strcmp(const char* cmp1, const char* cmp2)
{
assert(cmp1 && cmp2);//断言是否为空指针
while (*cmp1 != '\0'&& *cmp2 != '\0' && *cmp1 == *cmp2)//条件
{
cmp1++;
cmp2++;
}
return *cmp1 - *cmp2;//返回大小
}
int main()
{
const char* str1 = "abcdefabcd";
const char* str2 = "abcdefa";
int ret = my_strcmp(str1, str2);
if (ret > 0)
{
printf("str1 > str2\n");
}
else if (ret < 0)
{
printf("str1 < str2\n");
}
else
{
printf("str1 == str2\n");
}
return 0;
}
2.4 strncpy、strncat和strncmp
2.4.1 介绍
strncpy、strncat、和strncmp这些库函数是在前面函数的基础上加了一个控制拷贝(拼接、对比)字节个数功能,其他功能不变。
2.4.2 strncpy模拟
这里我只写一个模拟,其他模拟都是类似的,可以类比,首先我们要知道strncpy这个库函数底层,(char*)strncpy((char* dest),(char* src),(size_t num));可以知道返回值是一个指针,多了一个字节个数,接下来直接开始模拟
#include<stdio.h>
char* my_strncpy(char* dest, char* src,size_t num)
{
char* ret = dest;//拷贝
while (num--)//用字节来控制
{
*dest = *src;
dest++;
src++;
}
*dest = '\0';//主动加
return ret;
}
int main()
{
char str1[20];//目标
char str2[] = "abcdef";//源
char* ret = my_strncpy(str1, str2,4);
printf("%s\n", ret);
return 0;
}
2.4.3 与strcpy、strcat、strcmp比较
如果是比较的话,可以通过strcpy和strncpy:
strncpy相比strcpy更加安全(也是用于拷贝字符串),格式为strncpy(用于拷贝地址(指针),字符串,允许复制的最大字符数)对于strncpy当用于拷贝的地址大小小于字符串大小时可以,这个函数会自动填充\0,大于等于时就不会将\0填充到拷贝地址内(这里大于等于是指单纯字符串不包括\0)。
strcpy的优点:简单易用,只需要提供源字符串和目标字符串即可完成复制;自动在目标字符串末添加\0,确保字符串终止
缺点:不检查目标字符串的大小,可能导致导致缓和区溢出:如果源字符串长度超过目标字符串大小,可能会导致未定义行为
strncpy的优点:可以指定要复制的字符数,避免缓和区溢出的风险;可以手动控制目标字符串的终止符,增加了灵活性。
缺点:如果源字符串长度小于指定的字符数,strncpy会在目标字符串中添加额外的\0,可能导致目标字符串过长;如果源字符串长度大于指定的字符串数,目标字符串可能不会以\0结尾,导致字符串未终止。
同样的对于strcat和strcmp可以通过类比来比较。
2.5 strlen 3种模拟
第一种通过循环寻找\0:
#include<stdio.h>
int my_strlen(const char* str)
{
int count = 0;//计数
while (*str != '\0')
{
count++;
str++;
}
return count;
}
int main()
{
const char str[] = "abcdef";
int len = my_strlen(str);
printf("%d\n", len);
return 0;
}
第二种通过指针减指针绝对值为元素个数:
#include<stdio.h>
int my_strlen( char* str1)
{
char* str2 = str1;//记录起始
while (*str1 != '\0')
{
str1++;
}
return str1 - str2;//指针相减的绝对值为元素个数
}
int main()
{
char str[] = "abcdef";
int len = my_strlen(str);
printf("%d\n", len);
return 0;
}
第三种就是通过递归的思想,每一次的调用加一:
#include<stdio.h>
int my_strlen(char* str)
{
if (*str == '\0')//递归的限制
return 0;
else
return 1 + my_strlen(str + 1);
}
int main()
{
char str[] = "abcdef";
int len = my_strlen(str);
printf("%d\n", len);
return 0;
}
以上运行的代码都是一样的全为6,所以这给我了启发,我们要善于多用几种方法去解决一道题。
strlen返回值是size_t类型的,两个size_t类型相减也是size_t,而size_t类型是无符号的,所以使用时候要注意
2.6 strstr
2.6.1 strstr库函数使用
strstr库函数用来寻找是否有相同的字符串,格式为(char*)strstr(const字符串str1(比较),const字符串str2(寻找的))如果寻找到相同的字符串,那就会返回起始str1中str2的起始字符开始读取直到读到\0停止,如果没有找到则返回NULL空指针。
#include<stdio.h>
#include<string.h>
int main()
{
char str1[] = "aabaceebcccabace";
char str2[] = "bac";
char* ret = strstr(str1, str2);
printf("str1:%s\n", str1);
printf("ret:%s\n", ret);
return 0;
}
2.6.2 strstr库函数的模拟
strstr(str1,str2)底层,就是通过一个一个比较字符,如果str1第一个与str2的第一个匹配失败那就换str1第二个进行比较,如果在途中匹配成功了就有三种情况了,第一种:就是一路匹配成功(这是最好情况),第二种就比较麻烦了,那就是中途又失败,那就str2还要返回到第一个数中,而str1要返回到刚开始第一个匹配成功的那个值然后加一,继续重新匹配第一个数,然后最后匹配成功(情况最复杂的),第三种就是匹配第二个的时候到达末尾了(也就是不相同情况)。很抽象,直接上图:
接下来就好好分析我为什么这样子搞,前面两种情况是最好理解的,直接对第三种分析。第三种这我们arr1和arr2是不动的值(记录起始位置的),那么我们比较时就需要再设2个指针来指向地址,然后解析进行比较(这里我设置了s1(arr1),s2(arr1)这两个),在之前我们分析了在匹配到第二个时匹配失败了,我们的处理方式了,这里就是要找一个指针去记录第一个匹配成功的地方,那就是每一次匹配成功一次后用一个指针记录(这里我用的是cur记录),这里不只是要记录还需要跳过一个字符,进而在让s1退回到cur这个地址,这下思路就顺了,剩下的细节我都写在代码里面去了。
#include<stdio.h>
#include<assert.h>
const char* my_strstr(const char* arr1, const char* arr2)
{
assert(arr1 && arr2);//判断是不是空指针
const char* s1 = NULL;//用于arr1比较
const char* s2 = NULL;//用于arr2比较
const char* cur = arr1;//cur初始
while (*cur)
{
s2 = arr2;
s1 = cur;//返回
while (*s1 == *s2 && *s1 != '\0')
{
s1++;
s2++;
}
if (*s2 == '\0')
{
return cur;//返回
}
cur++;//
}
return NULL;
}
int main()
{
const char arr1[] = "aabaceebcccabace";
const char arr2[] = "cca";
const char* ret = my_strstr(arr1,arr2);
printf("%s\n", ret);
return 0;
}
2.7 strtok库函数使用
strtok这个库函数用来分隔符号(也就是字符)的函数,格式为(char*)strtok(char*str(需要拷贝),const char*sep(这个是需要分割的字符)),而对于strtok遇到sep内分割符号后就会将其改为\0,返回分割符号之前的str这个起始指针,当strtok遇到第一个为不是空指针时,那么就会寻找到分割符号将其改为\0,并在编译器中保留这个位置,当遇到第一个是空指针时,那么strtok这个就会按照上一个保留的位置开始,并跳过空指针,寻找下一个分割符号。代码如图
#include<stdio.h>
#include<string.h>
int main()
{
char str1[] = "2133714913@qq.com";
char str2[20];
const char* sep = "@.";
strcpy(str2, str1);
char* ret1 =strtok(str2, sep);
printf("%s", ret1);
char* ret2 = strtok(NULL, sep);//上一个保留了位置
printf("%s", ret2);
char* ret3 = strtok(NULL, sep);//同上
printf("%s", ret3);
return 0;
}
这样的代码是很糙的代码,只是方便理解,一般我们都是用循环写:
#include<stdio.h>
#include<string.h>
int main()
{
char str1[] = "2133714913@qq.com";
const char* sep = "@.";
char str2[20];
strcpy(str2, str1);
char* p = NULL;
for (p = strtok(str1, sep); p != NULL; p= strtok(NULL, sep))
// 初始化 判断 调整
{
printf("%s", p);
}
return 0;
}
2.8strerror函数使用
strerror库函数是用来返回错误信息的地址,这个一般和errno这个全局变量一起使用,这里errno这个全局变量,又需要头文件errno.h,一旦程序出现什么错误,errno就会有数字,然后strerror(errno)将错误数字的错误消息地址返回,然后用指针来接受,进而打印。
#include<errno.h>
int main()
{
FILE* pf = fopen("test.test", "r");
//成功访问则返回非空指针
//否则为空指针
if (pf == NULL)
{
printf("%s\n", strerror(errno));
return 1;
}
//perror("错误信息");
//这个函数是用于直接设置名字,并且还能打印出对应的错误信息
return 0;
}