目录
1.前言
大家平时在刷题的时候是否遇到一些比较麻烦的处理字符,字符串函数的题目。比如,将字符进行分类,大小写转化,计算字符串个数,拷贝字符串等。其实C语言提供了一系列库函数来帮组程序员实现对字符,字符串的快速处理。今天我们就来着重了解它们,并试着进行模拟实现。
2. 字符类
2.1字符分类函数
表格中的函数使用都非常类似,我们就以函数isupper为例:
int isupper (int c);
isupper是用来判断参数c是否为大写字母的。若参数c为大写字母,则返回非0整数;不为大写字母,则返回0。
#include<ctype.h>
int main()
{
char a;
scanf("%c", &a);
if (isupper(a))//函数返回值最为判断条件
printf("大写字母\n");
else
printf("不为大写字母\n");
return 0;
}
模拟实现思路 :
因为字符在内存中以ASCII值的形式储存,并且大小写字母的ASCII值是连续的,那么我们直接判断字符的ASCII值是否在该范围内,从而判断该字符是否为大(小)字母。
大写字母 A~Z ASCII:65~90 小写字母:a~z ASCII:97~122
2.2字符转换函数
我们在刷题时会遇到将字符进行大小写转换,而C语言中提供了两个字符转换函数——tolower toupper。
int tolower(int c);//参数为大写字母,则返回其对应的小写字母
int toupper(int c);//参数为小写字母,则返回其对应的大写字母
模拟实现思路 :
因为同一个字母的大小写的ASCII刚好相差32,所以我们只需要将字母的ASCII值加上(减去)32就能获得对应的小写(大写)字母。
下面运用字符分来函数和字符转换函数来实现将一个字符串进行大小写转化:
int main()
{
char arr[15] = "Hello World";
int i = 0;
while (arr[i])
{
if (islower(arr[i]))
arr[i]=toupper(arr[i]);
else
arr[i]=tolower(arr[i]);
i++;
}
printf("%s", arr);
return 0;
}
3.字符串类
3.1 strlen函数的模拟实现
模拟实现思路:
思路一:首先想到使用指针,又因为字符串的特性——以'/0'结尾,我们可以利用这一点。即让计算机从首字符开始往后数,遇到'/0'后停下,再使用局部变量记录个数,最后返回用来计数的局部变量即可。
size_t M_strlen(char* p)
{
size_t count = 0;
while (*p++)
{
count++;
}
return count;
}
int main()
{
char arr[] = "abcdef";
printf("%zd",M_strlen(arr));
return 0;
}
思路二: 思路一采用的是迭代,其实也可以尝试使用递归来实现。
#include<assert.h>
size_t m_strlen(char* p)
{
assert(p);//防止p为空指针
if (*p == 0)//限制条件
return 0;
else
return 1 + m_strlen(p + 1);
}
①这里函数递归的限制条件是解引用指针p得到的值不为0,而字符串的结束标志'/0'的ASCII值就是0。所以递归越深入,参数就会越接近限制条件。②因为我们的设想是每当遇到一个不为'/0'的字符时,函数返回值加1,否则就保持不变(加0)。所以不满足限制条件就直接返回0,反之就是1加上下一层函数递归的返回值。
3.2 strcpy函数的使用和模拟实现
#include<string.h>
int main()
{
char arr1[] = "xxxxxxxxxxxxxxx";
char arr2[] = "hello world";
strcpy(arr1, arr2);
printf("%s", arr1);
return 0;
}
这里直接打印“hello world” ,说明拷贝源字符串时也包含'/0'。
注意 :
模拟实现思路:
我们只需要将源字符串中的字符逐一地赋值到目标字符串中对应的字符内即可。
#include<string.h>
#include<assert.h>
char* m_strcpy(char* destination, const char* source)
{
assert(destination);
assert(source);
char* p = destination;
while (*destination++ = *source++)
{//先解引用在加加访问下一个字符
;//空语句
}
return p;//返回目标字符串的起始地址
}
int main()
{
char arr1[] = "xxxxxxxxxxxxxxx";
char arr2[] = "hello world";
m_strcpy(arr1, arr2);
printf("%s", arr1);
return 0;
}
3.3 strcat函数的使用和模拟实现
#include<string.h>
int main()
{
char arr1[10] = "acde";
char arr2[] = "fghi";
strcat(arr1, arr2);
printf("%s\n", arr1);
strcat(arr1, arr1);//尝试字符串自己连接自己
printf("%s\n", arr1);
return 0;
}
注意 :
模拟实现思路:
想要实现字符串连接,只需要从目标字符串的'/0'处开始将源字符串中的字符逐一赋值到目标字符串中即可。
#include<string.h>
char* m_strcat(char* destination, const char* source)
{
char* p = destination;
while (*++destination)
{
;
}//此时的指针指向目标字符串的/0处
while (*destination++ = *source++)
{
;
}
return p;//返回目标字符串起始位置
}
int main()
{
char arr1[10] = "acde";
char arr2[] = "fghi";
printf("%s", m_strcat(arr1, arr2));
return 0;
}
3.4strcmp函数的使用和模拟实现
逐一比较字符串中对应字符的ASCII 值,在第一次出现两字符不相等的情况时,若str1中字符的ASCII值大于str2中的,则返回大于0的整数;反之则返回小于0的整数。
int main()
{
char arr1[] = "abc";
char arr2[] = "abd";
printf("%d",strcmp(arr1, arr2));
return 0;
}
模拟实现思路 :
比较两个字符串,实际上是比较每个字符串对应位置字符的ASCII值的大小。只需要将对应位置字符的ASCII值相减,若结果不为0,则直接返回相减后的结果;若为0,则往后继续比较,直到遇到终止字符'/0'
int m_strcmp(const char* arr1, const char* arr2)
{
while (*arr1 && *arr2)//遇到终止字符前
{
if (*arr1 - *arr2)
{//相减后的结果不为0时,返回该结果
return *arr1 - *arr2;
}
arr1++;
arr2++;
}
return 0;
}
int main()
{
char arr1[] = "abc";
char arr2[] = "abd";
printf("%d",m_strcmp(arr1, arr2));
return 0;
}
3.5strstr函数的使用和模拟实现
.
int main()
{
char arr1[] = "abcdebcde";
char arr2[] = "cde";
printf("%s", strstr(arr1, arr2));
return 0;
}
模拟实现思路:
注:实现字符串查找有一种非常高效的KMP算法,由于本人才疏学浅,还没有理清缘由,所以这里只能给大家带来遍历法,实在抱歉!
在字符串str1中找到第一次出现的字符串str2,首先就需要再str1中找到与str2中首字符相等的字符,再往后比较第二,三个字符,若之后的字符与str2中的均相等,则返回str1中与str2中首字符相等的指针;若之后的字符与str2中的不相等,则在str1中继续往后寻找与str2首字符相等的字符,直到满足条件,或者到达str1的末尾。
char* m_strstr( const char* str1,const char* str2)
{
char* p2 = (char*)str2;//const char*需要强制转化为char*
char* p1 = (char*)str1;
if (!*str2)//*str2为/0时
return ((char*)str1);
while (*(char*)str1++)//未到达字符串str1末尾
{
p1 = (char*)str1;//指针p1回str1下一字符的位置
p2 = (char*)str2;//指针p2回到起始位置
while (*p2)//未到达字符串str2末尾
{
if (*p2 != *p1)
break;
p2++;
p1++;
}
if (*p2 == 0)
return (char*)str1;
}
return NULL;
}
int main()
{
char arr1[] = "abcdecdecde";
char arr2[] = "cde";
printf("%s", m_strstr(arr1, arr2));
return 0;
}
3.6strtok函数的使用
1 char * strtok ( char * str, const char * sep);
注意:
int main()
{
char arr[] = "2023.12.26";
char* sep = ".";
printf("%s\n", strtok(arr, sep));//第一个参数不为NULL,找到第一个标记
printf("%s\n", strtok(NULL, sep));//第一个参数为NULL,找到同一字符串中下一标记
printf("%s\n", strtok(NULL, sep));
printf("%s\n", strtok(NULL, sep));//不存在更多标记,返回空指针
return 0;
}
3.7strerror函数的使用
char * strerror ( int errnum );
我们来打印0~10这些错误码的对应信息
#include<errno.h>
int main()
{
int i = 0;
for (i = 0; i <= 10; i++)
{
printf("%2d:%s\n", i,strerror(i));
}
return 0;
}
使用举例:
#include <string.h>
#include <errno.h>
int main()
{
FILE* pFile;
pFile = fopen("unexist.text", "r");//以阅读的方式打开不存在的文件unexist.text
if (pFile == NULL)
printf("% s\n", strerror(errno));
return 0;
}
4.总结
我们介绍完了部分处理字符,字符串的库函数。在对其部分进行模拟实现的时候很容易发现,实现这些功能离不开指针。同时,也希望能在各位对指针的理解和使用上产生一点帮助。