在我们写代码的过程中,有时候遇到了关于字符和字符串的问题时,处理起来就会不太方便,这是c语言标准库就提供了一系列的库函数,方便我们使用。
字符函数:
函数 | 头文件<ctype.h> |
islower | 判断是否为小写字符,如果是返回非0的数,反之返回0 |
isupper | 判断是否为大写字符,如果是返回非0的数,反之返回0 |
tolower | 将大写字母转换为小写字母 |
toupper | 将小写字母转换为大写字母 |
在我们之前需要将字符串全部转换为大写字母的是时候,我们可能这样写代码
//'A' = 65 'a' = 97
//'B' = 66 'b' = 98
int main()
{
char arr[] = "i Am a StUdent";
int i = 0;
while (arr[i] != '\0')
{
//判断字母是否是小写
if (arr[i] >= 'a' && arr[i] <= 'z')
{
//大写字母与小写字母的ASCII值相差32
arr[i] = arr[i] - 32;
}
i++;
}
printf("%s\n", arr);
return 0;
}
我们只能通过它们之间ASCII值来进行大小写的转换。
但是我们学习了上面的字符函数之后代码就可以这样写
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\n", arr);
return 0;
}
字符串函数:
strlen()函数
头文件是<string.h>
参数: size_t strlen ( const char * str );
str --- 要计算长度的字符串
返回一个size_t类型的值
作用:统计'\0'之前出现的字符个数
我们简单写一个代码运用strlen()函数
int main()
{
char arr[] = "abcdef";
int ret = strlen(arr);
printf("%d", ret);
return 0;
}
这里我们在运用strlen()函数的时候有时候会出现问题
int main()
{
if (strlen("abc") - strlen("abcdef") > 0)
{
printf("<\n");
}
else
{
printf(">=\n");
}
return 0;
}
我们看到第一个strlen函数计算的结果应该是3,第二个strlen函数计算的结果应该是6,3-6 = -3, -3<0所以打印的会是>=吗?
结果怎么是 >
解析
这里我们要知道strlen()函数返回的值是size_t,是无符号整数,那么两个无符号整数相减得到的也会是一个无符号的整数,虽然它是-3,但是它是无符号整数,肯定是会比0大的,所以结果是<
这里我们学习了strlen()函数,那我们可以不可以模拟实现strlen()函数呢?
strlen()函数模拟实现:
方法一:计数法
思路:我们先创建一个变量当作计数器,用来记录循环的次数,通过循环让指针走向'\0'之后退出循环,这时,循环的次数就是字符串的个数
int my_strlen(const char* str)//这里加上const防止字符串被修改
{
//首先断言str是否为空指针
assert(str);
//这里我们创建一个计数器
int count = 0;
while (*str != '\0') //遇到'\0'退出循环
{
count++;
//进入循环count++
str++;
//让指针往后走
}
return count;
}
int main()
{
char arr[] = "abcdef";
int ret = my_strlen(arr);
printf("%d", ret);
return 0;
}
方法二:递归
这个方法不需要创建临时变量
思路:我们需要计算字符串"abcdef"的长度,我们就可以拆分为计算1+"bcdef",同理"bcdef"也可以拆分为计算1+"cdef"...理解了这个思路之后,我们就可以进行递归操作了,这个让递归停下来的条件就是指针走到了'\0'的位置,然后我们需要让指针一直往后走
int my_strlen(const char* str)
{
assert(str);
if (*str == '\0')
return 0;
else
return 1 + my_strlen(++str);
}
int main()
{
char arr[] = "abcdef";
int ret = my_strlen(arr);
printf("%d", ret);
return 0;
}
方法三:指针-指针
思路:我们需要两个指针,并且他们都是指向字符串首元素的,然后让其中的一个指针往后走到'\0\,再让两个指针相减,得到的个数就是字符串的个数
int my_strlen(const char* str)
{
assert(str);
//我们创建一个指针保存字符串首元素的地址
char* p = str;
while (*str)
{
str++;
//这里让指针一直往后走
}
//出循环之后刚好指针str在'\0'的位置
return str - p;
}
int main()
{
char arr[] = "abcdef";
int ret = my_strlen(arr);
printf("%d", ret);
return 0;
}
strcpy()函数
头文件:<string.h> 目的地 源头
参数:char * strcpy ( char * destination, const char * source );
destination: 指向用于存储拷贝内容的目标数组
source:要拷贝的字符串
返回一个指向目的地首元素的指针
作用:把source指向的字符串拷贝到destination指向的目标数组中。
条件:
1.源头指向的字符串必须以'\0'结尾
2.destination指向的目标数组空间必须足够大并且可修改
简单写一个代码使用strcpy()函数
#include<string.h>
int main()
{
char arr1[20] = "hello";
char* arr2 = "abcdef";
strcpy(arr1, arr2);//将arr2中的字符串拷贝到arr1里面
printf("%s", arr1);
return 0;
}
strcpy()函数模拟实现
思路:我们想要把src的内容拷贝到dest里面去,首先这两个肯定都不能是空指针,
然后利用循环,将src里面的值循环赋值给dest,当最后把'\0'赋值完了之后,条件为假,跳出循环,在循环前,我们也需要创建一个指针变量记录dest指向的目标数组的起始位置
char* my_strcpy(char* dest, const char* src)
{
//判断dest和src是否是空指针
assert(dest && src);
//创建指针变量p保存dest指向的目标数组的起始地址
char* p = dest;
while (*dest++ = *src++)//循环赋值,当把'\0'赋值完之后,判断条件为假,跳出循环
{
;
}
return p;
}
int main()
{
char arr1[20] = "hello";
char* arr2 = "abcdef";
char* p = my_strcpy(arr1, arr2);
printf("%s", p);
return 0;
}
strcat()函数(字符串追加)
头文件:<string.h>
参数:char * strcat ( char * destination, const char * source );
作用:将source指向的字符串追加到destination指向的目标数组中
简单写一个代码使用strcat()函数
int main()
{
char arr1[20] = "hello ";
char* arr2 = "world";
strcat(arr1, arr2);//将arr2里面的字符串追加到arr1里面
printf("%s", arr1);
return 0;
}
strcat()函数模拟实现
思路:我们想要把src的内容追加到dest里面去,首先我们要找到dest指向的数组中'\0'的位置,在\0的位置把我们的src里面的值循环赋值给dest
char* my_strcat(char* dest, const char* src)
{
//判断dest和src是否为空指针
assert(dest && src);
//创建指针变量p保存dest指向的目标数组的起始地址
char* p = dest;
//我们先找到dest指向的目标数组中'\0'的位置
while (*dest)
{
//让*dest往后一直走,直到遇到'\0'跳出循环
dest++;
}
//这时的dest刚好指向'\0'
while (*dest++ = *src++)//循环赋值,直到把'\0'赋值给dest后,判断为假跳出循环
{
;
}
return p;
}
int main()
{
char arr1[20] = "hello ";
char* arr2 = "world";
char*p = my_strcat(arr1, arr2);
printf("%s", p);
return 0;
}
strcmp()函数
头文件:<string.h>
参数 :int strcmp ( const char * str1, const char * str2 );
作用:比较str1指向的字符串和str2指向的字符串的大小
1.如果str1>str2,返回一个大于0的数字
2.如果str1 == str2,返回0
3.如果str1<str2,返回一个小于0的数字
两个字符串的比较,其实是比较两个字符串中对应位置上字符ASCII码值的大小
简单写一个代码使用strcmp()函数
int main()
{
char arr1[] = "abcdef";
char arr2[] = "abq";
int ret = strcmp(arr1, arr2);//比较arr1和arr2
printf("%d", ret);
return 0;
}
为什么是-1,因为q的ASCII码值比c的ASCII码值大,后面的就不会比了。
strcmp()函数模拟实现
思路:我们知道,两个字符串的比较,其实是比较两个字符串中对应位置上字符ASCII码值的大小,那么一个一个的进行比较
int my_strcmp(const char* str1, const char* str2)
{
//判断str1和str2是否为空指针
assert(str1 && str2);
while (*str1 == *str2)//str1 == str2才会进入循环
{
if (*str1 == '\0')//这里如果str1等于\0的话,那么str2也等于0了,提前返回0
{
return 0;
}
//让它俩往后走
str1++;
str2++;
}
//如果没进入循环直接相减
return *str1 - *str2;
}
int main()
{
char arr1[] = "abcdef";
char arr2[] = "abq";
int ret = my_strcmp(arr1, arr2);
printf("%d", ret);
return 0;
}
以上介绍的这些函数都属于长度不受限制的字符串函数。
strncpy()函数
参数:char * strncpy ( char * destination, const char * source, size_t num );
这个函数和上面的strcpy函数多了一个参数num。
num:要拷贝的个数
作用:把source指向的字符串拷贝num个到destination指向的目标数组中
简单写个代码使用strncpy()函数
int main()
{
char arr1[20] = "abcde";
char* arr2 = "xyzfn";
strncpy(arr1, arr2, 3);//拷贝3个字符到arr1里面去
printf("%s", arr1);
return 0;
}
假如source指向的字符串长度小于num个,则拷贝完source指向的字符串之后,在destination指向的字符串的后面追加\0,直到num个。
strncat()函数
参数: char * strncat ( char * destination, const char * source, size_t num );
作用:将source指向的字符串追加num个到destination指向的目标数组中
简单写个代码使用strncat()函数
int main()
{
char arr1[20] = "hello ";
char* arr2 = "world";
strncat(arr1, arr2, 3);//追加3个字符到arr1里面去
printf("%s", arr1);
return 0;
}
假如source指向的字符串长度小于num个,只会将source指向的字符串中到\0的内容追加到destination指向的字符串指向的末尾。
strncmp()函数
参数:int strncmp ( const char * str1, const char * str2, size_t num );
作用:最多比较num个str1指向的字符和str2指向的字符的大小
简单写个代码使用strncmp()函数
int main()
{
char* arr1 = "abcdef";
char* arr2 = "abcdqf";
int cmp = strncmp(arr1, arr2, 4);//比较arr1和arr2的前4个字符的大小
printf("%d", cmp);
return 0;
}
比较arr1和arr2的前num个字符,如果相等就继续往后比较,最多比较num个字符,如果提前发现不一样,就提前结束
int main()
{
char* arr1 = "abcdef";
char* arr2 = "abqdef";
int cmp = strncmp(arr1, arr2, 5);//这里我们比较arr1和arr2的前5个字符
printf("%d", cmp);
return 0;
}
提前发现不一样,提前返回了。
以上的这些就是长度受限制的字符串函数
strstr()函数
参数:const char * strstr ( const char * str1, const char * str2 );
作用:在str1中查找str2是否出现过,函数返回字符串str2在字符串str1中第一次出现的位置,如果str2不在str1中出现,则会返回一个空指针。
简单写个代码使用strstr()函数
int main()
{
char arr1[] = "i am a student";
char* arr2 = "am";
char* pc = strstr(arr1, arr2);//在arr1中查找arr2是否出现过
if (pc != NULL)
{
printf("%s", pc);
}
else
{
printf("找不到!\n");
}
return 0;
}
strstr()函数模拟实现
思路:我们这里先创建一个指针变量cp,用来记录str1开始匹配的位置,然后还得需要两个指针变量s1,s2循环比较,如果*str1 != *str2,cp++,从str1的起始位置后一个字符开始匹配,如果*s2 == \0,说明匹配成功,如果cp走到\0,就代表str1中没有出现str2.
char* my_strstr(const char* str1, const char* str2)
{
//这里先让cp指针保存str1的起始位置
const char* cp = str1;
//这里先设置两个空指针
const char* s1 = NULL;
const char* s2 = NULL;
//如果这里传过来的str2为空字符串,直接返回str1
if (*str2 == '\0')
//类型不一样,需要强转成char*类型的
return (char*)str1;
while (*cp)
{
//这里将s1和s2都指向两个字符的起始位置
s1 = cp;
s2 = str2;
while(*s1!='\0'&&*s2!='\0'&& * s1 == *s2)
{
//这里如果两个指针相等,就让他们往后走继续比对
s1++;
s2++;
}
if (*s2 == '\0')//这里*s2走到\0代表str字符串已经遍历完了提前返回
{
return (char*)cp;
}
cp++;
}
//这里就是str1字符串走完了表示没有匹配成功
return NULL;
}
int main()
{
char arr1[] = "abcde";
char* arr2 = "bcd";
char* pc = my_strstr(arr1, arr2);//在arr1中查找arr2是否出现过
if (pc != NULL)
{
printf("%s", pc);
}
else
{
printf("找不到!\n");
}
return 0;
}
strtok()函数
参数:char * strtok ( char * str, const char * delimiters );
str:指向要被分割的字符串
delimiters:指向⼀个字符串,定义了⽤作分隔符的字符集合
作用:将str指向的字符串分割成一系列的字串
返回值:该函数返回被分割的第一个子字符串,如果没有可检索的字符串,则返回一个空指针
简单写一个代码实现strtok()函数
int main()
{
char arr[] = "owq.cc.@.com";
const char* sep = ".@";//这里分隔符不分前后,也可以写成@.
char* ret = NULL;
for (ret = strtok(arr, sep); ret != NULL; ret = strtok(NULL, sep))
{
printf("%s\n",ret);
}
return 0;
}
1.strtok函数找到str中的第一个标记,并将其修改成 \0 ,然后返回⼀个指向这个标记的指针。2.strtok函数的第二次调用,前面的参数就写成NULL ,strtok函数将在第一次被标记的位置开始,查找下一个标记。
3.如果字符串中不存在更多的标记,则返回 NULL 指针
简单来说 strtok函数需要多次调用,strtok()函数可以将目标字符串,以分隔符为标记,分成一系列的字串。
strerror()函数
头文件:<errno.h>
参数:char * strerror ( int errnum );
errnum:错误码 (一般用errno来表示)
作用:可以将参数部分错误码对应的错误信息的字符串地址返回来,strerror()生成的错误字符串取决于编译器。
简单写个代码使用strerror()函数
int main()
{
int i = 0;
for (i = 0; i < 5; i++)
{
printf("%d: %s\n", i, strerror(i));
}
return 0;
}
1. No error 没有错误
2.Operation not permitted 操作不允许
3.No such file or directory 没有这样的文件
4.No such process 没有这样的过程
5.Interrupted function call 中断功能调用
strerror()函数常用于动态内存开辟和文件操作
int main()
{
FILE* pf = fopen("text.c", "r");//以读的形式打开名为text.c的文件
if (pf == NULL)
{
printf("%s\n", strerror(errno));
return 1;
}
//读文件
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
刚好对应我们的错误码5, 没有这样的文件
perror函数也可达到相似的效果,perror函数打印完参数部分的字符串之后,还会打印一个冒号和一个空格,再打印错误信息。
int main()
{
FILE* pf = fopen("text.c", "r");//以读的形式打开名为text.c的文件
if (pf == NULL)
{
perror("fopen");
return 1;
}
//读文件
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}