常见的字符串函数有:strlen 、strcpy 、strcmp 、strcat 、strstr 、strtok 、strerror 、strcasecamp、strncat 、strncmp、strncpy、strncasecmp,还包括一些字符串分类函数
(!!!左边有目录!!!)
先介绍前12种的模拟实现
功能:求字符串长度,返回的是unsigned int(无符号整形)
使用方法:
#include
#include
int main()
{
char str[] = "hello world";
int len = strlen(str); //求str的长度
printf("%d\n",len); //打印结果 11
}
模拟实现:字符串其实是以 ‘\0’ 为结尾的数组,我们可以有三种方法实现
1)计数器实现
#include
unsigned int my_strlen(char str[]) //因为字符串长度始终为正,所以我们返回值为无符号整形
{
int count = 0; //计数器
int i = 0;
for(i = 0;str[i] != '\0';i++)
{
count++; //当str[i]等于'\0'时,说明计数器到达字符串末尾,返回count
}
return count;
}
int main()
{
char str[] = "hello world";
int len = my_strlen(str);
printf("%d", len); //输出结果 11
}
2)指针实现
#include
unsigned int my_strlen(const char* src) //因为我们只是求字符串长度,不需要对字符串进行操作,所以用const
{
int i = 0;
char* pt = src; //src存的是字符串首字符的地址
while(*pt != '\0') //当指针指到 '\0'时结束循环
{
pt++;
}
return pt - src;
}
int main()
{
char str[] = "hello world";
int len = my_strlen(str);
printf("%d",len); //结果为11
}
3)递归实现(需要用到指针)
#include
unsigned int my_strlen(const char* str)
{
if(*str == '\0') //递归出口
return 0;
else
return 1 + my_strlen(str + 1);
}
int main()
{
char str[] = "hello world";
int len = my_strlen(str);
printf("%d",len); //结果为11
}
功能:将一个字符串拷贝到另一个字符串中,它的返回值是拷贝后字符串的地址
例:
#include
#include //注意需要包含头文件
int main()
{
//下面将字符串b的类容拷贝到a中,注意b的长度不能大于a的长度,否则会发生数组越界
char a[] = "hello world";
char b[] = "123456";
printf("拷贝前a:%s\n", a);
char* pt = strcpy(a,b); //注意我们用指针变量来接受返回的地址
printf("拷贝后a:%s\n", a);
}
模拟实现:
拷贝的实质其实是将b的每个地址的字符赋值到a中对应位置,包括 ‘\0’
#include
char* my_strcpy(const char* a,const char* b) //注意返回类型为指针
{
int i = 0;
while(*a = *b) //当 *b 等于 '\0'时,*a = *b 这个表达式的结果就为0,0为假所以推出while循环
{
a++;
b++;
}
/*上面的while循环等价于
while(*b != '\0')
{
*a = *b;
a++;
b++;
} */
}
int main()
{
char a[20] = "hello world";
char b[] = "123456";
printf("拷贝前a:%s\n", a);
char* pt = strcpy(a,b); //注意我们用指针变量来接受返回的地址
printf("拷贝后a:%s\n", a);
}
功能:比较两个字符串大小,从两个字符串的首字符开始比较(比较它们的ASCII码值的大小),如果相等,则比较下一个字符,如果第一个大于第二个,则返回一个大于0的数,否则返回小于0的数。当两个字符串相等时,返回0;
注意:!!!这个函数是会区分大小写的,例如 a 就大于B
例如:strcmp(“hello” , “apple”) , 返回一个大于0的数 (这个数会因为编译器的不同而不同,有的编译器是2,有的是16)
#include
#include //注意引头文件
int main()
{
char a[] = "apple";
char b[] = "boy";
int ret = strcmp(a , b); //a小于b,返回小于0的数
printf("strcmp(a , b): %d\n", ret);
ret = strcmp(b , a); //b大于a,返回一个大于0的数
printf("strcmp(b , a): %d\n", ret);
ret = strcmp(a , a); //a等于a,返回0
printf("strcmp(a , a): %d\n", ret);
}
模拟实现:
strcmp函数的本质是依次比较字符的大小,如果第一个大于第二个,返回大于0的值,如果第一个小于第二个,返回小于0的值
#include
int my_strcmp(const char* p1,const char* p2)
{
char* a = p1;
char* b = p2;
while(*a != '\0' && *b != '\0') //当两个字符相等时,且不等于 '\0' ,继续比较后面的字符
{
if(*a > *b) //第一个大于第二个,返回大于0的值
return 1;
else if(*a < *b) //第一个小于第二个,返回小于0的值
return -1;
else //第一个等于第二个,继续比较后面的
{
a++;
b++;
}
}
//我们还要考虑a,b长度不相等的情况
if(*a != '\0')
return 1; //a的前半截跟b一样,后面比b长,所以a大于b,返回大于0的数
if(*b != '\0')
return -1;
return 0; //当两个字符串相等时,返回 0
}
int main()
{
char a[] = "hello world";
char b[] = "hello";
int ret = my_strcmp(a , b);
if(ret > 0)
printf("a 大于 b\n");
else if(ret < 0)
printf("a 小于 b\n");
else
printf("a 等于 b\n");
}
功能:strcat(a , b)在字符串 a 后面追加字符串 b,例如 strcat(“hello” , “world”) 结果为 “helloworld”,它的返回值是追加后字符串的地址
例子:
#include
//下面将字符串a追加到字符串b的后面,注意要保证a的长的足够大,否则会发生数组越界!!!
int main()
{
char a[50] = "I love ";
char b[] = "you";
puts(a);
strcat(a , b); //将字符串b追加到a的后面
puts(a);
}
模拟实现:
模拟这个函数其实就是找的字符串 a 的 '\0’位置,然后从这里开始拷贝b
#include
char* my_strcat(const char* a, const char* b) //返回类型为指针变量
{
char* p1 = a;
char* p2 = b; //因为a,b为常量,所以新定义两个指针变量替代a,b
while (*p1) //当指针指向的位置不为'\0'时,指针指向下一个位置
{
p1++;
}
//此时s中保存的a中'\0'的地址
while (*p2) //此循环完成拷贝过程
{
*p1++ = *p2++;
}
*p1 = '\0'; //注意!!!前面的while循环是没有把'\0'拷贝过去的,所以拷贝完之后要在a的末尾添加结束标志
return a;
}
int main()
{
char a[50] = "I love ";
char b[] = "you";
puts(a);
my_strcat(a, b); //将字符串b追加到a的后面
puts(a);
}
功能:在字符串中查找其字串的位置,并返回字串的地址
例如:
#include
#include //注意头文件不要忘了
int main()
{
char a[] = "An apple a day keeps the doctor away";
char b[] = "doctor";
char* ret = strstr(a , b);
puts(ret);
}
模拟实现:
这个稍微麻烦一些,看代码吧
#include
char* my_strstr(const char* a, const char* b)
{
char* p1 = a;
char* p2 = b;
char* cu = a; //cu为当前指针
//查找方法,从a的第一个字符串开始检索,直到'\0'
while(*cu != '\0')
{
p1 = cu;
p2 = b;
while(*p1 != '\0' && *p2 != '\0' && *p1 == *p2) //当两个字符相等时,依次向后检索,直到b的末尾
{
p1++;
p2++;
}
if(*p2 == '\0')
{
return cu;
}
cu++;
}
return NULL; //返回NULL表示没找到
}
int main()
{
char a[] = "An apple a day keeps the doctor away";
char b[] = "doctor";
char* ret = strstr(a , b);
if(ret != NULL)
puts(ret);
else
printf("没找到!\n");
}
功能:根据分界符将字符串分割成一个个片段,例如 “192.20.50.60” 按 ‘.’ 分割为 “192 20 50 60”
这个字符串分割函数的使用要复杂一些,下面我们看代码
#include
#include
int main()
{
char a[] = "192.20.50.60"; //第一种分隔符只有一种
char* p1 = ".";
char* ret = strtok(a , p1);
printf("a: ");
for(ret;ret != NULL;ret = strtok(NULL,p1))
{
printf("%s ", ret);
}
printf("\n");
char b[] = "123#123&123"; //第二种分隔符有多种
char* p2 = "#&";
char* res = strtok(b , p2);
printf("b: ");
while(*p2) //当*p2 != '\0'时执行循环
{
for(res;res != NULL;res = strtok(NULL,p2)) //这里面跟上面的a一样
{
printf("%s ", res);
}
p2++;
}
}
功能:首先说errno这是一个全局变量,表示当前的错误代码,当C语言库函数执行出错时(这种错误不是程序本身的错误,程序还是可以编译运行的),这个变量会被赋值为相应的错误码,而strerror的作用就是把错误码转换成一句我们能看懂话
例如:
#include
#include
int main()
{
//用strerror这个函数将错误码转化为字符串
printf("%s\n",strerror(0));
printf("%s\n",strerror(1));
printf("%s\n",strerror(2));
printf("%s\n",strerror(3));
}
下面看一个打开文件的例子:
#include
#include //注意使用errno需要引这个头文件
#include
int main()
{
//打开文件出错时
FILE* p = fopen("test.txt", "r"); //打开名为test.txt的文件,r表示只读
printf("%s\n",strerror(errno)); //用strerror这个函数将错误码转化
}
功能:比较两个字符串的大小,只不过和strcmp不同的是它不区分大小写,例如用这个函数比较 “hello” 和 "Hello"是相等的
例如:
#include
#include
int main()
{
char a[] = "hello";
char b[] = "Hello";
//先用strcasecmp比较
int ret = strcasecmp(a, b);
if(ret > 0)
printf("a大于b\n");
else if(ret < 0)
printf("a小于b\n");
else
printf("a等于b\n");
//再用strcmp比较
ret = strcmp(a, b);
if(ret > 0)
printf("a大于b\n");
else if(ret < 0)
printf("a小于b\n");
else
printf("a等于b\n");
}
模拟实现:思路很简单,把小写全部转化为大写(或者大写全部转换为小写就好了)
#define _CRT_SECURE_NO_WARNINGS 1
#include
#include
int my_strcasecmp(const char* p1, const char* p2)
{
int i = 0;
//为了避免破坏原字符串,我们先拷贝一份
char a[50];
char b[50];
strcpy(a, p1);
strcpy(b, p2);
//在strcmp的基础上添加小写转大写的操作
for (i = 0; a[i] != '\0'; i++)
if (a[i] > 96 && a[i] < 123)
a[i] -= 32;
for (i = 0; b[i] != '\0'; i++)
if (b[i] > 96 && b[i] < 123)
b[i] -= 32;
i = 0;
while (a[i] != '\0' && b[i] != '\0') //当两个字符相等时,且不等于 '\0' ,继续比较后面的字符
{
if (a[i] > b[i]) //第一个大于第二个,返回大于0的值
return 1;
else if (a[i] < b[i]) //第一个小于第二个,返回小于0的值
return -1;
else //第一个等于第二个,继续比较后面的
i++;
}
if (a[i] != '\0') //这里跟模拟strcmp一样
return 1;
if (b[i] != '\0')
return -1;
return 0; //当两个字符串相等时,返回 0
}
int main()
{
char a[] = "hello";
char b[] = "hi";
int ret = my_strcasecmp(a, b);
if (ret > 0)
printf("a 大于 b\n");
else if (ret < 0)
printf("a 小于 b\n");
else
printf("a 等于 b\n");
}
功能:这个和刚刚strcat 的区别是限制了拼接的字节数,例如strcat(a , b , 20) 就是将字符串b拼接到a后面,但是后面接上去的字符串最多只能为20个字节
例如:
#include
#include
int main()
{
char a[50] = "hello ";
char b[] = "zhangsan"; //我们可以算出来b占9个字节
strncat(a , b , 5);
puts(a); //因为限定了5个字节,所以这里只能将zhang拼上去,这里输出hello zhang
strncat(a , b , 9);
puts(a); //这里输出hello zhangzhangsan
}
输出结果:
模拟实现:在之前模拟实现的函数内部加一些限制函数就好了
#include
char* my_strncat(const char* a, const char* b, const int n) //返回类型为指针变量
{
int count = n; //加一个计数器
char* p1 = a;
char* p2 = b;
while (*p1)
{
p1++;
}
//此时s中保存的a中'\0'的地址
while (*p2 && count>0) //这里加一个条件
{
*p1++ = *p2++;
count--;
}
*p1 = '\0'; //注意!!!前面的while循环是没有把'\0'拷贝过去的,所以拷贝完之后要在a的末尾添加结束标志
return a;
}
int main()
{
char a[50] = "I love ";
char b[] = "you";
int n = 1;
puts(a);
my_strncat(a, b, n); //将字符串b追加到a的后面,限制大小为n字节
puts(a);
}
功能:这个跟strcmp的区别也是限定了比较的字符串长度为n个字节
例如:
#include
#include //注意头文件
int main()
{
char a[] = "hello world";
char b[] = "hello wangwu";
int ret = strncmp(a, b, 7);
printf("%d\n",ret); //前七个字符都相等,此时应打印0
ret = strncmp(a, b, 8);
printf("%d\n",ret); //第八个字符不相等,此时打印一个大于0的数
}
运行结果:
模拟实现:我们直接在刚才strcmp的模拟代码上改动
#include
int my_strncmp(const char* p1, const char* p2, const int n)
{
char* a = p1;
char* b = p2;
int count = n;
while (*a != '\0' && *b != '\0' && count > 0) //当两个字符相等时,且不等于 '\0' ,继续比较后面的字符
{
count--;
if (*a > *b) //第一个大于第二个,返回大于0的值
return 1;
else if (*a < *b) //第一个小于第二个,返回小于0的值
return -1;
else //第一个等于第二个,继续比较后面的
{
a++;
b++;
}
}
return 0; //当两个字符串相等时,返回 0
}
int main()
{
char a[] = "hello world";
char b[] = "hello";
int n = 6;
int ret = my_strncmp(a, b, n); //比较前五个字符
if (ret > 0)
printf("a 大于 b\n");
else if (ret < 0)
printf("a 小于 b\n");
else
printf("a 等于 b\n");
}
功能:strncpy限制了拷贝字符串的长度,返回值为拷贝后字符串的地址,使用方法:char* ret = strncpy(a, b, n);
例子:
#include
#include
int main()
{
char a[] = "hello world";
char b[] = "123456";
puts(a);
char*ret = strncpy(a, b, 5); //把b前五个字节内容拷贝到a中去
puts(a);
}
运行结果:
模拟实现:同样在strcpy的基础上进行改动,思路还是加一个计数器来控制拷贝的字符串长度
#include
char* my_strncpy(const char* p1,const char* p2, const int n) //注意返回类型为指针
{
char* a = p1;
char* b = p2;
int count = n;
while(*a != '\0' && *b != '\0' && count) //*a,*b,count都不为0时执行循环
{
*a++ = *b++; //等价于*a = *b; *a++; *b++;
count--;
}
//注意考虑特殊情况,当n大于a的长度时,b中的'\0'是没有拷贝到a里面去的
//所以我们拷贝结束的位置手动添加一个 '\0'
*a = '\0'; //当n小于a时这句话也不会对程序产生影响
}
int main()
{
char a[20] = "hello world";
char b[] = "123456";
int n = 5; //只拷贝b的前五个字节的内容
printf("拷贝前a:%s\n", a);
char* pt = my_strncpy(a, b, n); //将b中前五个字节的内容拷贝到a
printf("拷贝后a:%s\n", a);
}
功能:这个其实比较简单,它就是比较两个字符串的第n个字符(忽略大小写),如果相等,则返回0;如果第一个大于第二个,返回一个大于0的数;如果第一个小于第二个,返回一个小于0的数
例子:
#include
#include
int main()
{
char a[] = "apple";
char b[] = "application";
int n = 5;
int ret = strncasecmp(a, b, n); //比较a和b的第五个字符
printf("%d\n", ret);
}
模拟实现:
#include
int my_strncasecmp(const char* a,const char* b,const int n)
{
char* p1 = a + n;
char* p2 = b + n;
//此时p1和p2都指向对应字符
//注意这个函数的比较时不区分大小写的
//我们可以把他们全部转化为大写字母来比较
if(*p1 > 96 && *p1 < 123) //这个如果记不住的话可以去参考一下ASCII码表
*p1 -= 32;
if(*p2 > 96 && *p2 < 123)
*p2 -= 32;
//下面进行比较
if(*p1 > *p2)
return 1;
else if(*p1 < *p2)
return -1;
else
return 0;
}
int main()
{
char a[] = "hello world";
char b[] = "hello lisi";
int n = 8;
int ret = my_strncasecmp(a, b, n); //比较a和b的第n个字符
if(n > sizeof(a) || n > sizeof(b)) //当n超出了a或者b的长度时,程序报错
{
printf("error!\n");
return 0;
}
if(ret > 0)
printf("%c大于%c\n", a[n-1], b[n-1]);
else if(ret < 0)
printf("%c小于%c\n", a[n-1], b[n-1]);
else
printf("%c等于%c\n", a[n-1], b[n-1]);
}