C语言常用字符串函数解析(包括模拟实现)

C语言常用字符串函数解析(包括模拟实现)

常见的字符串函数有:strlen 、strcpy 、strcmp 、strcat 、strstr 、strtok 、strerror 、strcasecamp、strncat 、strncmp、strncpy、strncasecmp,还包括一些字符串分类函数

(!!!左边有目录!!!)

先介绍前12种的模拟实现

1.strlen\strcpy\strcmp\strcat\strstr\strtok\strerror

1.strlen

功能:求字符串长度,返回的是unsigned int(无符号整形)

使用方法:

#include<stdio.h>
#include<string.h>
int main()
{
    
    
    char str[] = "hello world";
    int len = strlen(str);  //求str的长度
    printf("%d\n",len);     //打印结果 11
}

模拟实现:字符串其实是以 ‘\0’ 为结尾的数组,我们可以有三种方法实现

1)计数器实现

#include<stdio.h>
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<stdio.h>
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<stdio.h>
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
}

2.strcpy

功能:将一个字符串拷贝到另一个字符串中,它的返回值是拷贝后字符串的地址

例:

#include<stdio.h>
#include<string.h> //注意需要包含头文件
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<stdio.h>
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);
}

3.strcmp

功能:比较两个字符串大小,从两个字符串的首字符开始比较(比较它们的ASCII码值的大小),如果相等,则比较下一个字符,如果第一个大于第二个,则返回一个大于0的数,否则返回小于0的数。当两个字符串相等时,返回0;
注意:!!!这个函数是会区分大小写的,例如 a 就大于B

例如:strcmp(“hello” , “apple”) , 返回一个大于0的数 (这个数会因为编译器的不同而不同,有的编译器是2,有的是16)

#include<stdio.h>
#include<string.h>   //注意引头文件
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<stdio.h>
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");
}

运行结果:

4.strcat

功能:strcat(a , b)在字符串 a 后面追加字符串 b,例如 strcat(“hello” , “world”) 结果为 “helloworld”,它的返回值是追加后字符串的地址

例子:

#include<stdio.h>

//下面将字符串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<stdio.h>
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);
}

运行结果:

5.strstr

功能:在字符串中查找其字串的位置,并返回字串的地址

例如:

#include<stdio.h>
#include<string.h>  //注意头文件不要忘了
int main()
{
    
    
    char a[] = "An apple a day keeps the doctor away";
    char b[] = "doctor";
    char* ret = strstr(a , b);
    puts(ret);
}

运行结果:

模拟实现:

这个稍微麻烦一些,看代码吧

#include<stdio.h>
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");
}

运行结果:

6.strtok

功能:根据分界符将字符串分割成一个个片段,例如 “192.20.50.60” 按 ‘.’ 分割为 “192 20 50 60”

这个字符串分割函数的使用要复杂一些,下面我们看代码

#include<stdio.h>
#include<string.h>
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++;
    }
    
}

运行结果:

7.strerror

功能:首先说errno这是一个全局变量,表示当前的错误代码,当C语言库函数执行出错时(这种错误不是程序本身的错误,程序还是可以编译运行的),这个变量会被赋值为相应的错误码,而strerror的作用就是把错误码转换成一句我们能看懂话

例如:

#include<stdio.h>
#include<string.h>
int main()
{
    
    
     //用strerror这个函数将错误码转化为字符串
    printf("%s\n",strerror(0)); 
    printf("%s\n",strerror(1)); 
    printf("%s\n",strerror(2)); 
    printf("%s\n",strerror(3)); 
}

输出结果:

下面看一个打开文件的例子:

#include<stdio.h>
#include<errno.h>   //注意使用errno需要引这个头文件
#include<string.h>
int main()
{
    
    
    //打开文件出错时
    FILE* p = fopen("test.txt", "r");   //打开名为test.txt的文件,r表示只读
    printf("%s\n",strerror(errno));    //用strerror这个函数将错误码转化
}

当源代码所在文件夹没有test这个文件时,运行结果如下:

当源代码所在文件夹有test这个文件时,运行结果如下:

8.strcasecmp

功能:比较两个字符串的大小,只不过和strcmp不同的是它不区分大小写,例如用这个函数比较 “hello” 和 "Hello"是相等的

例如:

#include<stdio.h>
#include<string.h>
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<stdio.h>
#include<string.h>
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");
}

运行结果:在这里插入图片描述

2.strncat\strncmp\strncpy\strncasecmp

1.strncat

功能:这个和刚刚strcat 的区别是限制了拼接的字节数,例如strcat(a , b , 20) 就是将字符串b拼接到a后面,但是后面接上去的字符串最多只能为20个字节

例如:

#include<stdio.h>
#include<string.h>
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<stdio.h>
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);
}

运行结果:

2.strncmp

功能:这个跟strcmp的区别也是限定了比较的字符串长度为n个字节

例如:

#include<stdio.h>
#include<string.h>     //注意头文件
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<stdio.h>
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");
}

运行结果:在这里插入图片描述

3.strncpy

功能:strncpy限制了拷贝字符串的长度,返回值为拷贝后字符串的地址,使用方法:char* ret = strncpy(a, b, n);

例子:

#include<stdio.h>
#include<string.h>
int main()
{
    
    
    char a[] = "hello world";
    char b[] = "123456";
    puts(a);
    char*ret = strncpy(a, b, 5);  //把b前五个字节内容拷贝到a中去
    puts(a);
}

运行结果: 在这里插入图片描述
模拟实现:同样在strcpy的基础上进行改动,思路还是加一个计数器来控制拷贝的字符串长度

#include<stdio.h>
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);
}

运行结果:在这里插入图片描述

4.strncasecmp

功能:这个其实比较简单,它就是比较两个字符串的第n个字符(忽略大小写),如果相等,则返回0;如果第一个大于第二个,返回一个大于0的数;如果第一个小于第二个,返回一个小于0的数

例子:

#include<stdio.h>
#include<string.h>
int main()
{
    
    
    char a[] = "apple";
    char b[] = "application";
    int n = 5;
    int ret = strncasecmp(a, b, n);  //比较a和b的第五个字符
    printf("%d\n", ret);
}

运行结果:在这里插入图片描述
返回了一个小于0的数

模拟实现:

#include<stdio.h>
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]);
}

运行结果:在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_52698632/article/details/113408894