在学习编程的过程中,我们会经常会遇到一些字符和字符串,为了方便操作字符和字符串,C语言标准库中就提供了一系列函数。那么,接下来就学习下这些函数。
1. 字符分类函数
C语言中有一系列的函数是专门做字符分析的,也就是判断一个字符属于什么类型的字符的。
这些函数的使用都需要包含一个头文件 ctype.h 。
上图就是字符类函数的一些种类。
这些函数的使用方法非常类似,我们就以一个函数为例。
int main()
{
int ret1 = islower('c');
int ret2 = islower('A');
printf("ret1 = %d\n", ret1);
printf("ret2 = %d\n", ret2);
return 0;
}
islower函数 是一个能判断参数部分是否为小写字母的函数。
通过返回值来说明是否为小写字母,如果是小写字母,就返回一个非0的整数,如果不是小写字母,就返回数字0。
2. 字符转换函数
C语言提供了两个字符转换函数:
int tolower ( int c ); //将参数传进去的⼤写字⺟转⼩写
int toupper ( int c ); //将参数传进去的⼩写字⺟转⼤写
多说不用,直接上一道例题体会体会。
练习:将一个字符串的小写字母转换成大写字母,其他字符不变。
int main()
{
char arr[] = "hello man";
int i = 0;
while (arr[i])
{
if (islower(arr[i]))
{
arr[i] = toupper(arr[i]);
}
i++;
}
printf("%s", arr);
return 0;
}
上面代码就是通过 toupper 这个函数将字符串中的小写字母转换成大写代码的例子。
运行如下
3. strlen的使用和模拟实现
size_t strlen ( const char * str );
1.strlen函数是用来计算一个字符串大小的库函数,不过它计算的是 ‘\0’ 前面的大小,不包括 ‘\0’,所以用strlen来计算大小是以 ‘\0’ 为结束标志的。
2.参数指向的字符串必须要以 '\0' 结束。
3.注意strlen函数的返回值是 size_t 类型的。这是一个易错点。
4. strlen 的使用需要包含其头文件 <string.h>
为什么说第3点是一个易错点呢?直接上代码
#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;
}
上面这段代码运行结果是什么呢?
相信很多人就立马猜出打印 str1>str2。 到底是不是呢?
运行代码,发现并不是我们猜想的那样。到底是为什么呢?
哦,原来是strlen函数的返回值类型为size_t的原因,size_t 是无符号整形的意思,两个无符号整形的相减的结果还是无符号整形的,尽管原来的结果是负数,但被强制转换成了size_t类型的,结果中带有的负号就不再算是符号位了,也就不算是一个 负数了。这就导致了运行结果的不同。
strlen函数的3中模拟实现
1. 计算器类型
int my_strlen(const char * str)
{
int count = 0;
assert(str);
while(*str)
{
count++;
str++;
}
return count;
}
2,递归类型
int my_strlen(const char * str)
{
assert(str);
if(*str == '\0')
return 0;
else
return 1+my_strlen(str+1);
}
3.指针-指针类型
int my_strlen(char *s)
{
assert(str);
char *p = s;
while(*p != ‘\0’ )
p++;
return p-s;
}
4.strcpy的使用和模拟
char* strcpy(char * destination, const char * source );
strcpy函数的作用是将一个字符串里面的内容复制到另一个字符串里面。 如上形式,就是将source里面字符串里的内容复制到destination字符串。
使用时注意事项
1. 源字符串必须以 '\0' 结束。
2.strcpy不仅会将字符串的内容复制到另一个函数,也会将 '\0' 一同复制过去。
3.目标空间必须可修改,如果目标空间是一个常量,就无法使用strcpy函数。
4.目标空间必须足够大,有确保能保存复制过来的内容。
strcpy的模拟实现
实现模拟之前。我们要清楚strlen函数是将字符串里面的内容一个一个复制过来的。
void my_strcpy( char* dest, char* soc)
{
while (*soc != '\0')
{
soc++;
dest++;
}
*dest = *soc; // 将 '\0' 复制过来
}
int main()
{
char arr1[] = "xxxxxxx";
char arr2[20] = "abcd";
my_strcpy(arr2, arr1);
printf("%s", arr2);
return 0;
}
我们也可以简化成以下版本
void my_strcpy( char* dest, char* soc)
{
while (*dest++==*soc++)
{
;
}
}
int main()
{
char arr1[] = "xxxxxxx";
char arr2[20] = "abcd";
my_strcpy(arr2, arr1);
printf("%s", arr2);
return 0;
}
简化版本也会将 '\0' 复制过去的,虽然++的优先级比 * 好高,但它是后置++,所以是先用完指定数据在进行++,也就是先对dest和soc进行解引用之后,再分别++,由于while()循环只有条件为真时才能进行,所以当soc一直++到 '\0' 时,赋值给dest时,条件刚好为0,循环终止,dest 也刚好被赋值 '\0' , 也就将arr2中的 '\0' 复制过去了。
以 %s 的形式打印时,也是以 '\0' 为终点的。
运行如下
5.strcat的使用和模拟
char * strcat ( char * destination, const char * source );
strcat()函数是用来拼接2个字符串的函数。
使用strcat的注意事项
1. 源字符串必须要以 '\0' 结尾。
2. 目标空间也必须要有 ‘\0' 结尾,否则不知道从哪里开始拼接。
3. 目标空间要有足够大的空间来存储源字符串复制过来的内容。
4. 目标空间必须是可修改的空间。
我们还是直接通过代码感受
int main()
{
char arr1[20] = "abcd";
char arr2[] = "efghigk";
strcat(arr1, arr2);
printf("%s", arr1);
return 0;
}
这段代码是将arr2里面的内容拼接到arr1里面。
运行代码如下图
通过上面图片可知,arr2的内容已经成功拼接到arr1里面了。
模拟实现strcat函数
模拟之前,先大概清楚下strcat()函数是如何实现拼接的。
首先,我们要找到目标空间的 '\0' ,确定了开始拼接的位置,然后再一个一个将要拼接的内容拼接到目标空间。
了解这个,开始实现代码
void my_strcat(char* dest, const char* soc)
{
assert(dest && soc);
//找到目标空间的 \0
while (*dest)
{
dest++;
}
//接着开始实现复制,复制就和前面的strcmp的一摸一样了
while ((*dest++ = *soc++))
{
;
}
}
int main()
{
char arr1[20] = "abcd";
char arr2[] = "efghigk";
my_strcat(arr1, arr2);
printf("%s", arr1);
return 0;
}
6. strcmp的使用和模拟
int strcmp ( const char * str1, const char * str2 );
strcmp是用来比较2个字符串的大小的。
两个字符串的比较原理
两个字符串相互比较时,两个字符串中的相应字符会一个一个进行比较,如上图,直到遇到两个不同的字符,这时,那个字符的ASCII值大,对应的字符串就大。如上图,c的ASCII值大于b的,则下面的字符串大。
strcmp是有返回值的。
标准规定:
1.当第一个字符串 小于 第二个字符串时,返回一个小于0的值。
2.当第一个字符串 等于 第二个字符串时,返回0.
3.当第一个字符串 大于 第二个字符串时,返回一个大于0的值。
照惯例,上代码
int main()
{
char arr1[20] = "abcd";
char arr2[] = "efghigk";
int ret=strcmp(arr1, arr2);
printf("%d", ret);
return 0;
}
由于arr1字符串小于arr2字符串,所以ret为一个小于0的值。
模拟实现strcmp()函数
int my_strcmp(char* arr1, char* arr2)
{
while (*arr1 == *arr2)
{
if (*arr1 = *arr2)
{
return 0;
}
arr1++;
arr2++;
}
if (*arr1 > *arr2)
{
return 1;
}
else
{
return -1;
}
}
int main()
{
char arr1[20] = "abcd";
char arr2[] = "abcd";
int ret=my_strcmp(arr1, arr2);
printf("ret=%d", ret);
return 0;
}
也可以简化成
int my_strcmp(char* arr1, char* arr2)
{
while (*arr1 == *arr2)
{
if (*arr1 = *arr2)
{
return 0;
}
arr1++;
arr2++;
}
return *arr1 - *arr2;
}
int main()
{
char arr1[20] = "abcd";
char arr2[] = "abcd";
int ret=my_strcmp(arr1, arr2);
printf("ret=%d", ret);
return 0;
}
因为是通过字符串里面的字符一个一个进行比较,所以想到用到了while()循环。
7.strncpy的使用
1 char * strncpy ( char * destination, const char * source, size_t num );
其实strncpy的使用和strcpy差不多,多了一个参数 num。
意思是将源字符串 num 个字符复制到目标空间。
注意事项
如果源字符串的长度小于num个,则会一直给目标空间补'\0',直到达到num个。
8.strncat的使用
1 char * strncat ( char * destination, const char * source, size_t num );
strncat的用法和strcat差不多,也是多了个参数num。
意思是将源字符串num个字符拼接到目标空间去,并追加一个 '\0' 。
注意事项
如果源字符串的长度小于num,则只会将 '\0' 前面的内容拼接到目标空间。
9.strncmp的使用
strncmp的用法和strcmp差不多,也是多了个参数num。
意思是⽐较str1和str2的前num个字符,如果相等就继续往后⽐较,最多⽐较num个字⺟,如果提前发现不⼀样,就提前结束,⼤的字符所在的字符串⼤于另外⼀个。如果num个字符都相等,就是相等返回0。
10.strstr的使用和模拟
1 char * strstr ( const char * str1, const char * str2);
strstr()函数是用来在一个字符串中寻找另一个字符串。
如果有则返回开始匹配的地址,如没有则返回NULL。
老样子,上代码
int main()
{
char arr1[20] = "abbbbhicd";
char arr2[] = "abb";
char* ret=strstr(arr1, arr2);
printf("%s", ret);
return 0;
}
如上图,在arr1字符串中寻找arr2字符串,由于当第一个字符匹配,再继续往后找时,就找到了abb,所以就将在arr1中开始匹配的地址返回,也就是将arr1中的a的地址返回。
运行结果如下
模拟实现strstr()
有几种情况我们要讨论一下
第一种情况是比较简单的,当发现第一个字符不匹配时,让arr1继续往后走,接着进行匹配就行了。
这种情况是最复杂的。
我们发现第一个字符不匹配时,让arr1+1,指向b,接着匹配,b和b匹配了, arr1和arr2一起进行加1,但发现匹配过程中有匹配不了了,所以这时我们要重新匹配,所以要让arr1和arr2共同返回开始匹配的位置,但是arr1和arr2已经在后面了,不在匹配时的位置了。
因此我们想到要用2个指针分别记录arr1中开始匹配的位置和arr2的起始地址。
如上图所示,用一个指针纪录开始匹配的位置,这样当我们匹配到半路失败时,可以返回的开始匹配地址。
char* my_strstr(const char* arr1,const char* arr2)
{
const char* s1 = NULL;
const char* s2 = NULL;
const char* cur = arr1;
if (*arr2 == '\0')
{
return (char*)arr1;
}
while (*cur)
{
s1 = cur;
s2 = arr2;
while (*s1 !='\0' && *s2!='\0' && * s1 == *s2)
{
s1++;
s2++;
}
if (*s2 == '\0')
{
return (char*)cur;
}
cur++;
}
return NULL;
}
int main()
{
char arr1[] = "abbbcefg";
char arr2[] = "bbc";
char* ret=my_strstr(arr1, arr2);
printf("%s", ret);
return 0;
}
11. strtok函数的使用
1 char * strtok ( char * str, const char * sep);
1.解释参数的意思
1.1 sep参数指向一个字符串,定义了一个用作分隔符的集合。
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;
}
12.strerror的使用
1 char * strerror ( int errnum );
int main()
{
int i = 0;
for (i = 0; i <= 10; i++) {
printf("%s\n", strerror(i));
}
return 0;
}