C语言进阶⑬(字符串函数)+(指针编程题)strlen+strcpy+strcat+strstr+strtok+strerror

1 指针编程练习:

1.1 字符串左旋

实现一个函数,可以左旋字符串中的n个字符。

例如:

ABCD左旋一个字符得到BCDA

ABCD左旋两个字符得到CDAB

法一(移首补尾法):


#include<stdio.h>
#include<string.h>
#include<assert.h>
void string_left_rotate(char* str, int n)
{
    assert(str != NULL);
    int sum = strlen(str);
    for (int i = 0;i < n;i++)//旋转几个字符就循环几次
    {
        char tmp = *str;//1.把第一个字符存起来
        for (int j = 0;j < sum - 1;j++)
        {
            *(str + j) = *(str + j + 1);//2.把第一个后面的字符依次往前挪动一位
        }
        *(str + sum - 1) = tmp;//3.把第一个字符放到最后
    }
}
int main()
{
    char arr[20] = "ABCDEF";
    printf("%s\n", arr);
    int n = 0;
    scanf("%d", &n);
    string_left_rotate(arr, n);
    printf("%s\n", arr);
    return 0;
}

法二(三步翻转法):


#include<stdio.h>
#include<string.h>
#include<assert.h>
void reverse(char* left, char* right)//字符串逆置(翻转)函数
{
    assert(left != NULL);
    assert(right != NULL);
    while (left < right)
    {
        char tmp = *left;
        *left = *right;
        *right = tmp;
        left++;
        right--;
    }
}
void string_left_rotate(char* str, int n)
{
    int sum = strlen(str);
    assert(str!=NULL);
    //如ABCDEF  n=2
    reverse(str, str+n-1);//1.翻转要左旋的n个字符  BA CDEF
    reverse(str+n, str + sum - 1);//2.翻转要剩余的个字符 BA FEDC
    reverse(str, str + sum - 1);//3.整体翻转  CDEF AB
}
int main()
{
    char arr[20] = "ABCEDF";
    printf("%s\n", arr);
    int n = 0;
    scanf("%d", &n);
    string_left_rotate(arr, n);
    printf("%s\n", arr);
    return 0;
}

1.2 字符串旋转结果

字符串旋转

写一个函数,判断一个字符串是否为另外一个字符串旋转之后的字符串。

例如:给定arr1 = AABCD和arr2 = BCDAA,返回1

给定arr1 = abcd和arr2 = ACBD,返回0.

AABCD左旋一个字符得到ABCDA

AABCD左旋两个字符得到BCDAA

AABCD右旋一个字符得到DAABC

法一(暴力穷举法):


//法一(暴力穷举法):
#include<stdio.h>
#include<string.h>
#include<assert.h>
int is_string_rotate(char* str1, char* str2)
{
    //穷举str1所有左旋的结果,然后和str2比较(右旋是左旋的特殊形式)
    assert(str1 != NULL);
    assert(str2 != NULL);
    int sum = strlen(str1);
    for (int i = 0;i < sum;i++)//旋转str1所有的结果
    {
        char tmp = *str1;//1.把第一个字符存起来
        for (int j = 0;j < sum - 1;j++)
        {
            *(str1 + j) = *(str1 + j + 1);//2.把第一个后面的字符依次往前挪动一位
        }
        *(str1 + sum - 1) = tmp;//3.把第一个字符放到最后

        //旋转一个字符比较一次
        if (strcmp(str1, str2) == 0)
        {
            return 1;
        }
    }
    return 0;//所有结果比较完,没有就返回0
}
int main()
{
    char arr1[20] = "ABCDEF";
    char arr2[20] = "CDEFAB";
    if (is_string_rotate(arr1, arr2))//是就返回1
    {
        printf("yes\n");
    }
    else
    {
        printf("no\n");
    }
    return 0;
}

法二:

法二用到很多字符串函数,笔试时可以更好展现自己,而且效率更高了

这些字符串函数的详细介绍都放在下面文章了


//法二:
#include<stdio.h>
#include<string.h>
#include<assert.h>
int is_string_rotate(char* str1, char* str2)
{
    assert(str1 != NULL);
    assert(str2 != NULL);
    if (strlen(str1) != strlen(str2))
    {
        return 0;   //长度不相等返回0
    }

    //1.给str1追加一个str1
    //如ABCDEF变成ABCDEFABCDEF  这样新字符串的其中6个就包含了原字符串旋转的所有可能
    strncat(str1, str1, strlen(str1));
    //字符串追加函数,在第一个参数后面追加第三个参数个第二个参数的字符串
    //(strcat不能给自己追加,strncat多最后一个参数)
    
    //2.比较str2是不是str1新字符串的子字符串(是的话就是str1原字符串旋转得来)
    char* ret = strstr(str1, str2);
    //判断第二个参数是否是第一个参数的子字符串
    //是的话返回第一个参数中子字符串的首地址,不是的话返回空指针
    //注意到如果长度不一样也是子字符串,不合题意,所以一开始判断长度

    return ret != NULL; //是空指针返回0,不是返回1
}
int main()
{
    char arr1[20] = "ABCDEF";
    char arr2[20] = "CDEFAB";
    if (is_string_rotate(arr1, arr2))//是就返回1
    {
        printf("yes\n");
    }
    else
    {
        printf("no\n");
    }
    return 0;
}

2 字符函数和字符串函数

下面讲的函数的头文件: string.h

重点介绍处理字符和字符串的库函数的使用和注意事项


求字符串长度

strlen


长度不受限制的字符串函数介绍

strcpy

strcat

strcmp


长度受限制的字符串函数介绍

strncpy

strncat

strncmp


字符串查找

strstr

strtok


错误信息报告

strerror


字符串操作:C语言中对字符和字符串的处理很是频繁,但是C语言本身是没有字符串类型的,

字符串通常放在常量字符串 中或者 字符数组 中。

字符串常量 适用于那些对它不做修改的字符串函数.


2.1 strlen三种实现

文档:size_t strlen ( const char * str );

1.字符串以 '\0' 作为结束标志,strlen函数返回的是在字符串中 '\0' 前面出现的字符个数

(不包含 '\0')。

2.参数指向的字符串必须要以 '\0' 结束。

3.注意函数的返回值为size_t,是无符号的( 易错 )

代码演示:


#include <stdio.h>
#include <string.h>
int main()
{
    int len = strlen("abcdef");
    printf("%d\n", len);
    return 0;
}

要注意的地方:


#include<stdio.h>
#include<string.h>
int main()
{
    const char* str1 = "abcd";
    const char* str2 = "abcdef";
    if (strlen(str1) - strlen(str2) > 0)
    {
        printf("str1>str2\n");
    }
    else
    {
        printf("srt1<=str2\n");
    }
    return 0;
}
//输出str1>str2 因为无符号减无符号还是无符号 大于0
//用自己模拟的strlen就不会出现这种问题

strlen函数的模拟实现:(三种方法)


#include<stdio.h>
#include<string.h>
#include<assert.h>
int my_strlen1(const char* str)//计数器版本
{
    assert(str != NULL);
    int count = 0;
    while (*str != '\0')
    {
        count++;
        str++;
    }
    return count;
}
int my_strlen2(const char* str)//递归版本
{
    assert(str != NULL);
    if (*str != '\0')
        return 1 + my_strlen2(str + 1);
    else
        return 0;
}
//左下右上转圈地看
//my(hello)
//my(hello)     1+my(ello) =5
//my(ello)      1+my(llo)  =4
//my(llo)       1+my(lo)   =3
//my(lo)        1+my(o)    =2
//my(o)  return 1+my(\0)   =1
//my(\0):return 0;
int my_strlen3(const char* str)//指针减指针版本
{
    assert(str != NULL);
    char* ret = str;
    while (*ret != '\0')
    {
        ret++;
    }
    return ret - str;
}
int main()
{
    char arr[] = "hello";
    printf("%d\n", strlen(arr));
    printf("%d\n", my_strlen1(arr));
    printf("%d\n", my_strlen2(arr));
    printf("%d\n", my_strlen3(arr));
    return 0;
}

2.2 strcpy模拟实现

文档:char* strcpy(char * destination, const char * source );

Copies the C string pointed by source into the array pointed by destination, including the

terminating null character (and stopping at that point).

源字符串必须以 '\0' 结束。

会将源字符串中的 '\0' 拷贝到目标空间。

目标空间必须足够大,以确保能存放源字符串。

目标空间必须可变。

代码演示:


#include <stdio.h>
#include <string.h>
int main()
{
    char arr1[] = "abcdefghi";
    char arr2[] = "123";
    printf("拷贝前:%s\n", arr1);//拷贝前:abcdefghi
    strcpy(arr1, arr2); // 字符串拷贝(目标空间,源字符串)
    printf("拷贝后:%s\n", arr1);//拷贝后:123
    return 0;
}

strcpy函数的模拟实现


#include<stdio.h>
#include<string.h>
#include<assert.h>
char* my_strcpy(char* dest, const char* src)
{
    assert(dest != NULL);
    assert(src != NULL);
    char* ret = dest;//让ret指向dest的起始地址
    while (*dest++ = *src++)//'\0'的ASCII码为0拷贝后跳出循环
    {
        ;
    }
    return ret;
}
int main()
{
    char arr1[50] = "#####################";
    char arr2[] = "hello world";
    //printf("%s\n", strcpy(arr1, arr2));
    printf("%s\n", my_strcpy(arr1, arr2));
    return 0;
}

2.3 strcat模拟实现

文档:char * strcat ( char * destination, const char * source );

Appends a copy of the source string to the destination string. The terminating null character in destination is overwritten by the first character of source, and a null-character is included at the end of the new string formed by the concatenation of both in destination.

源字符串必须以 '\0' 结束。

目标空间必须有足够的大,能容纳下源字符串的内容。

目标空间必须可修改。

字符串自己给自己追加,要用到strncat,用strcat的话 \0被覆盖了

代码演示:


#include <stdio.h>
#include <string.h>
int main()
{
    char arr1[30] = "hello";
    char arr2[] = "world";
    strcat(arr1, arr2);
    printf("%s\n", arr1);//helloworld
    return 0;
}

strcat函数模拟实现:


#include<stdio.h>
#include<string.h>
#include<assert.h>
char* my_strcat(char* dest, const char* src)
{
    assert(dest != NULL);
    assert(src != NULL);
    char* ret = dest;//让ret指向dest的起始地址
    while (*dest)//找到'\0'  '\0'的ASCII码为0跳出循环
    {
        dest++;
    }
    while (*dest++ = *src++)//'\0'的ASCII码为0拷贝后跳出循环
    {
        ;
    }
    return ret;
}
int main()
{
    char arr1[20] = "hello ";
    char arr2[] = "world";
    //printf("%s\n", strcat(arr1, arr2));
    printf("%s\n", my_strcat(arr1, arr2));
    return 0;
}

2.4strcmp模拟实现

文档:int strcmp ( const char * str1, const char * str2 );

This function starts comparing the first character of each string. If they are equal to each

other, it continues with the following pairs until the characters differ or until a terminating

null-character is reached.

标准规定:

第一个字符串大于第二个字符串,则返回大于0的数字

第一个字符串等于第二个字符串,则返回0

第一个字符串小于第二个字符串,则返回小于0的数字

代码演示:


#include <stdio.h>
#include <string.h>
int main()
{
    char* p1 = "abcdef";
    char* p2 = "aqwer";
    int ret = strcmp(p1, p2); // p1和p2比
    // a==a, 对比下一对,b<q,所以p2大
    printf("%d\n", ret);//-1

    return 0;
}

strcmp函数模拟实现:

自己想的笨方法:


#include<stdio.h>
#include<string.h>
#include<assert.h>
int my_strcmp(const char* str1, const char* str2)
{
    assert(str1 != NULL);
    assert(str2 != NULL);
    while (*str1 && *str2)
    {
        if (*str1 > *str2)
        {
            return 1;
        }
        else if (*str1 < *str2)
        {
            return -1;
        }
        else
        {
            str1++;
            str2++;
        }
    }
    if (*str1 == '\0' &&*str2 == '\0')//str1 str2都等于\0
    {
        return 0;
    }
    else if (*str1)//str2等于\0
    {
        return 1;
    }
    else//str1等于\0
    {
        return -1;
    }
}
int main()
{
    char arr1[] = "abcd";
    char arr2[] = "abcd";
    /*if (strcmp(arr1, arr2) > 0)
    {
        printf("arr1>arr2\n");
    }
    else if (strcmp(arr1, arr2) < 0)
    {
        printf("arr1<arr2\n");
    }
    else
    {
        printf("arr1=arr2\n");
    }*/
    if (my_strcmp(arr1, arr2) > 0)
    {
        printf("arr1>arr2\n");
    }
    else if (my_strcmp(arr1, arr2) < 0)
    {
        printf("arr1<arr2\n");
    }
    else
    {
        printf("arr1=arr2\n");
    }
    return 0;
}

看书后优化:


#include<stdio.h>
#include<string.h>
#include<assert.h>
int my_strcmp(const char* str1, const char* str2)
{
    assert(str1 != NULL);
    assert(str2 != NULL);
    while (*str1 == *str2)
    {
        if (*str1)//如果两个相等且其中一个为\0
        {
            return 0;
        }
        else
        {
            str1++;
            str2++;
        }
    }
    return *str1 - *str2;//返回>0或<0才是标准,而且可以看相差的ASCII码
}
int main()
{
    char arr1[] = "abcd";
    char arr2[] = "abcd";
    /*if (strcmp(arr1, arr2) > 0)
    {
        printf("arr1>arr2\n");
    }
    else if (strcmp(arr1, arr2) < 0)
    {
        printf("arr1<arr2\n");
    }
    else
    {
        printf("arr1=arr2\n");
    }*/
    if (my_strcmp(arr1, arr2) > 0)
    {
        printf("arr1>arr2\n");
    }
    else if (my_strcmp(arr1, arr2) < 0)
    {
        printf("arr1<arr2\n");
    }
    else
    {
        printf("arr1=arr2\n");
    }
    return 0;
}

前面三个都是长度不受限制的字符串函数

有点不安全

以下是三个长度受限制的字符串函数

2.5 strncpy

文档:char * strncpy ( char * destination, const char * source, size_t num );

从源字符串拷贝num个字符到目标空间。

Copies the first num characters of source to destination. If the end of the source C string

(which is signaled by a null-character) is found before num characters have been copied,

destination is padded with zeros until a total of num characters have been written to it.

注意事项:

① 如果源字符串的长度小于 n,则拷贝完源字符串之后,在目标的后面追加 0,填充至 n 个

② dest 和 src 不应该重叠(重叠时可以用更安全的 memmove 替代)

③ 目标空间必须足够大,以确保能够存放源字符串 dest

④ 目标空间必须可变,即目标空间 dest 不可以被 const 声明

代码演示:


#include <stdio.h>
#include <string.h>
int main()
{
    char arr1[5] = "abc";
    char arr2[] = "hello world";
    strncpy(arr1, arr2, 4); // 从arr2中拷贝4个到arr1
    printf("%s\n", arr1);// 打印了hell
    // 注意这里arr1有5个空间,如果arr1空间不够就会报错或者多打印其它东西
    return 0;
}

2.6 strncat

文档:char * strncat ( char * destination, const char * source, size_t num );

追加 num个字符到目标空间

Appends the first num characters of source to destination, plus a terminating null character.

If the length of the C string in source is less than num, only the content up to the terminating null-character is copied

注意事项:如果源字符串的长度小于 num,则只复制 \0 之前的内容。

代码演示:


#include <stdio.h>
#include <string.h>
int main()
{
    char arr1[30] = "hello";
    char arr2[] = "world";
    strncat(arr1, arr2, 3); // 从arr2中取3个追加到arr1中
    printf("%s\n", arr1);//打印了hellowor
    return 0;
}

2.7 strncmp

文档:int strncmp ( const char * str1, const char * str2, size_t num );

比较到出现另个字符不一样或者一个字符串结束或者num个字符全部比较完。

返回结果和strcmp一样


#include <stdio.h>
#include <string.h>
int main()
{
    const char* p1 = "abczdef";
    const char* p2 = "abcqwer";
    // int ret = strcmp(p1, p2);
    int ret1 = strncmp(p1, p2, 1);
    int ret2 = strncmp(p1, p2, 4);
    printf("%d %d\n", ret1, ret2);//0 1
    return 0;
}

2.8 strstr模拟实现

文档:char * strstr ( const char *str1, const char * str2);

在str1字符串中查找是否包含str2字符串,如果存在则返回的是第一次出现位置的字符串,如果不存在,则返回的是null,如果str2传的为空串则返回str1

Returns a pointer to the first occurrence of str2 in str1, or a null pointer if str2 is not part of str1.

代码演示:是子串的话,返回首次出现的地址


#include <stdio.h>
#include <string.h>
int main()
{
    char* p1 = "abcdefgh";
    char* p2 = "def";
    char* ret = strstr(p1, p2); // 判断p2是否是p1的子串
    printf("%s\n", ret);//defgh
    return 0;
}

思路:(双指针)(注意到是下面匹配上面)

配成功的首地址,这样我们在s1就需要用到双指针

一个用来记录上面s1第一次匹配的地址

这里以指针p来记录第n次匹配的起始位置;

大致思路是假设指针s1指向目标字符串首地址,指针s2指向源字符串首地址,分别对指针s1和s2解引用并比较,若匹配内容不相同称之为匹配失败,则目标字符串指针s1,向后移动一位,匹配内容相同称之为第n次匹配,假设第一次匹配成功则将第一次匹配中的指针s1和s2分别后移,若后移后指针s1和s2指向的内容不相同,则第一次匹配失败,s1回到第一次匹配的首位值得下一位,s2回到首地址,若此时指针s1和s2指向的内容相同则,开启第二次匹配,以此类推直到s2指向空,则匹配成功。


#include<stdio.h>
#include<string.h>
#include<assert.h>
char* my_strstr(const char* str1, const char* str2)
{
    assert(str1 != NULL);
    if (str2 == NULL)
    {
        return (char*)str1; //库函数里面的实现
    }     //强制类型转化为要返回的类型,本来类型是const char*
    const char* s1 = str1;
    const char* s2 = str2;
    const char* p = str1;
    while (*p)
    {
        s1 = p;        //一次匹配不成功p++后继续匹配
        s2 = str2;
        while (*s1 && *s2 && (*s1 == *s2))//其中一个为\0或者不相等就跳出
        {
            s1++;   //可以把两个++放到上面( )
            s2++;
        }
        if (*s2 == '\0') //匹配成功
        {
            return (char*)p;//强制类型转化为要返回的类型,本来类型是const char*
        }
        p++;         //一次匹配不成功p++后继续匹配
    }
    return NULL;
}
int main()
{
    char arr1[] = "abcdef";
    char arr2[] = "cd";
    if (strstr(arr1, arr2) == NULL)
    {
        printf("找不到");
    }
    else
    {
        printf("找到了:%s\n", strstr(arr1, arr2));
    }

    if (my_strstr(arr1, arr2) == NULL)
    {
        printf("找不到");
    }
    else
    {
        printf("找到了:%s\n", my_strstr(arr1, arr2));
    }
    return 0;
}


2.9 strtok

文档:char * strtok ( char * str, const char * sep );

注意事项:strtok 会破坏原字符串,分割后原字符串保留第一个分割符前的字符

分割邮箱使用演示:


#include<stdio.h>
#include<string.h>
int main()
{
    const char* sep = "/.";
    char email[] = "https://blog.csdn.net/GRrtx?type=blog";
    char cp[40] = { 0 };
    strcpy(cp, email);
    char* ret = strtok(cp, sep);
    if (ret != NULL)
        printf("%s\n", ret);

    ret = strtok(NULL, sep);
    if (ret != NULL)
        printf("%s\n", ret);

    ret = strtok(NULL, sep);
    if (ret != NULL)
        printf("%s\n", ret);

    ret = strtok(NULL, sep);
    if (ret != NULL)
        printf("%s\n", ret);

    ret = strtok(NULL, sep);
    if (ret != NULL)
        printf("%s\n", ret);

    return 0;
}

运行结果:

上面代码看书后用for优化后我的表情是这样的:


#include<stdio.h>
#include<string.h>
int main()
{
    const char* sep = "/.";
    char email[] = "https://blog.csdn.net/GRrtx?type=blog";
    char cp[40] = { 0 };
    strcpy(cp, email);
    for (char* ret = strtok(cp, sep); ret != NULL; ret = strtok(NULL, sep))
    {
        printf("%s\n", ret);
    }

    return 0;
}


2.10 strerror

文档:char * strerror ( int errnum );

返回错误码,所对应的错误信息。

C语言的库函数,在执行失败的时候,都会设置错误码,设置好的:

使用演示:


#include<stdio.h>
#include<string.h>
#include<errno.h>
int main()
{
    FILE* pf = fopen("test.txt", "r");//打开文件操作,后面会学
    if (pf == NULL)
    {
        printf("%s\n", strerror(errno));
        return 1;
    }
    fclose(pf);
    pf = NULL;
    return 0;
}


3 字符分类函数和字符转换函数

3.1字符分类函数

下面函数的头文件: #include <ctype.h> (这些函数不常用,刷题想不出来可以自己模拟实现)

代码演示:isupper


#include <stdio.h>
#include <ctype.h>
int main()
{
    char ch = 'R';
    if(isupper(ch))//是大写字母就返回非0  真
    {
        printf("是大写字母\n");
    }
    else
    {
        printf("不是大写字母\n");
    }
    return 0;
}


3.2字符转换函数

#include <ctype.h>

int tolower ( int c ); //大写转小写

int toupper ( int c ); //小写转大写


#include<stdio.h>
#include <ctype.h>
int main()
{
    int i = 0;
    char arr[] = "Test String.\n";
    while (arr[i] != '\0')
    {
        if (isupper(arr[i]))
        {
            arr[i] = tolower(arr[i]);
        }
        printf("%c", arr[i]);
        i++;
    }
    //运行结果:test string.
    return 0;
}


本篇完

练习就是自己模拟实现上面五种模拟实现的函数,其它的有时间就模拟下

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

GR鲸鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值