C的实用笔记37——几种常用的字符串处理API

Posted lzh201864031

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C的实用笔记37——几种常用的字符串处理API相关的知识,希望对你有一定的参考价值。

6.字符串拼接函数

0、知识点:

  1. 内存污染(存储字符串的目的内存不够用时,后面的内存会被污染,也就是被修改,类似于下标越界)

  2. strcat其实就是另一种形式的strcpy,一个从末尾开始复制,一个从头开始复制。

1、strcat函数:复习以下知识点后,练习 习题5,自己实现strncat()函数

  1. 函数原型:char*   strcat(char  *dest, const  char  *src);
  2. 操作数:①目的字符数组的首地址dest;②来源字符串的首地址src。
  3. 函数功能:把src所指向的字符串(包括“\\0”)拼接到dest所指向的字符串的末尾(末尾指的是dest所指字符串的第一个空字符'\\0',并删除*dest原来末尾的“\\0”),然后返回指向dest的指针。
  4. 缺点:①当*dest不足以容纳被复制进来的*src时,字符串会溢出,造成内存污染。尽量避免。
    #include <stdio.h>
    #include <string.h>
    int main(int argc, char const *argv[])
    
        char str[20] = "abcde";
        char *src = "hello";
        printf("拼接前:%s\\n", str);
        strcat(str, src);
        printf("拼接后:%s\\n", str);
        return 0;
    

2、strncat函数:复习以下知识点后,练习 习题6,自己实现strncat()函数

  1. 函数原型:char*   strncat(char  *dest, const  char  *src,  size_t   count);
  2. 操作数:①目的字符数组的首地址dest;②来源字符串的首地址src;③限制拼接的字节数count。
  3. 函数功能:把src所指向的字符串中以src地址开始的后count个字节,拼接到dest所指向的字符串末尾(删除*dest原来末尾的“\\0”)。返回指向dest的指针。
  4. 内部决策:当count大小 > src所指字符串长度时,多拼接的部分会用空字符'\\0'填充。
  5. 缺点:①当*dest不足以容纳被复制进来的*src时,字符串会溢出,造成内存污染。尽量避免。
    #include <stdio.h>
    #include <string.h>
    int main(int argc, char const *argv[])
    
        char str[20] = "abcde";
        char *src = "hello";
        printf("拼接前:%s\\n", str);
        strncat(str, src, 2);
        printf("拼接前2个字节后:%s\\n", str);
        return 0;
    

7.字符串比较函数

1、strcmp函数:复习以下知识点后,练习 习题7,自己实现strcmp()函数

  1. 函数原型:int   strcmp(const   char  *str1,  const  char  *str2);
  2. 操作数:①字符串1的首地址str1;②字符串2的首地址str2。
  3. 函数功能:逐字符比较str1 和 str2 指向的字符串中相同位置字符的ASCII码,如果第0个字符相同,就比较下一个字符,依此类推,直到比较出大小,若str1=str2,则返回0;若str1<str2,则返回-1;若str1>str2,则返回1。
  4. 使用方法:一般用来判断两个字符串是否一样,我们不关心两个字符串谁大谁小。
    #include <stdio.h>
    #include <string.h>
    int main(int argc, char const *argv[])
    
        char *str1 = "abcd2chen3";
        char *str2 = "abcdeche13567";
        int ret = strcmp(str1, str2);
        printf("RET=%d\\n", ret);
        if(ret == 0)
            printf("两个字符串一样\\n");
        else
            printf("两个字符串不一样");
        
        return 0;
    

2、strncmp函数:复习以下知识点后,练习 习题8,自己实现strncmp()函数

  1. 函数原型:int   strncmp(const   char  *str1,    const  char  *str2,   size_t   count);
  2. 操作数:①字符串1的首地址str1;②字符串2的首地址str2;③最大比较字节数count。
  3. 函数功能:逐字符比较str1 和 str2 指向的字符串中相同位置字符的ASCII码,如果第0个字符相同,就比较下一个字符,依此类推,最多比较前 count 个字节。若str1=str2,则返回0;若str1<str2,则返回-1;若str1>str2,则返回1。
  4. 使用方法:一般用来判断两个字符串的前count个字节是否一样,我们不关心两个字符串谁大谁小。
    #include <stdio.h>
    #include <string.h>
    int main(int argc, char const *argv[])
    
        char *str1 = "abcd2chen3";
        char *str2 = "abcdeche13567";
        int ret = strncmp(str1, str2, 10);
        printf("RET=%d\\n", ret);
        if(ret == 0)
            printf("两个字符串的前10个字符一样\\n");
        else
            printf("两个字符串的前10个字符不一样");
        
        return 0;
    

8.字符串查找函数

1、strchr函数:复习以下知识点后,练习 习题10,自己实现strchr()函数

  1. 函数原型:char*   strchr(const   char   *str,   int   c);
  2. 操作数:①字符串的首地址str;②被查找字符c的ASCII码。
  3. 函数功能:在字符指针 str 所指向的字符串中按照ASCII码,搜索第一次出现字符 c(一个无符号字符)的位置。并返回字符 c 的地址。
  4. 使用方法:①一般用来判断str所指字符串中是否含有字符c(如下示例)。②用来找出字符串str中所有字符c的个数和位置,见习题9
    #include <stdio.h>
    #include <string.h>
    int main(int argc, char const *argv[])
    
        char *str = "abcde12345 abcde12345 abcde12345";
        char *p = strchr(str, '3');
        if(p == NULL)
            printf("找不到字符'3'");
            return 0;
        
        printf("第一个字符'3'距离字符串首地址%d个字节", p - str);
        return 0;
    

2、strrchr函数:略。它与strchr唯一的不同就是从尾部开始查找,r是英文“rear”(尾部)的首字母。

3、strstr函数:复习以下知识点后,练习 习题11,自己实现strstr()函数

  1. 函数原型:char*   strstr(const   char   *str,   const   char   *substr);
  2. 操作数:①字符串的首地址str;②被查找的子字符串substr。
  3. 函数功能:在str指向的字符串中从头开始查找,若substr是str的子串,则返回substr在str中首次出现的地址。
  4. 使用方法:一般用来判断str所指字符串中是否含有子字符串substr。
    #include <stdio.h>
    #include <string.h>
    int main(int argc, char const *argv[])
    
        char *str = "abcde1234$#$ abcde5678$#$";
        char *substr = "$#$";
        char *p = strstr(str, substr);
        if(p == NULL)
            printf("找不到子字符串\\n");
            return 0;
        
        printf("第一个子字符串%s距离字符串首地址%d个字节", substr, p-str);
        return 0;
    

9.字符串分割函数

1、strtok函数:复习以下知识点后,练习习题12(解析信息),巩固strtok函数的用法

  1. 函数原型:char*   strtok(char  *str,   const   char  *delim);
  2. 操作数:①字符数组的首地址str;②分隔符的首地址delim。
  3. 函数功能:按照字符指针delim指向的分隔符中的字符对str所指字符串进行逐字符查找,如果在str中发现了delim中的字符,那么就使得str中的这个字符变成'\\0',函数结束并返回从str中切割出来的子字符串的首地址。
  4. 内部决策:①strtok函数遇到str中的空字符'\\0',则代表分割失败;②strtok函数具有记忆功能,在上一次切割完成后,strtok函数的起始分割位置会往后偏移,不能再将str所指字符串的首地址传递给strtok函数(因为会造成分割失败),strtok函数的设定是:再次调用strtok函数时,需要将NULL传进第一个参数中;③如果在str所指字符串中出现了连续的几个delim字符,strtok函数只会将第一个delim字符变成'\\0';
  5. 使用方法:只要没切到'\\0',就一直调用strtok函数,用一个字符指针数组来保存子字符串首地址。如图:
    #include <stdio.h>
    #include <string.h>
    int main(int argc, char const *argv[])
    
        char str[80] = "xiaoming:21,,,.男.,北京:haidian";
        const char *delim = ":,.";    //分隔符是冒号、逗号、句号
        char *token[10];            
        int i = 0;
        token[i] = strtok(str, delim);
        printf("第%d次切割,p[%d]=%s\\n", i+1, i, token[i]);
        while(token[i] != NULL)
            i++;
            token[i] = strtok(NULL, delim);
            printf("第%d次切割,p[%d]=%s\\n", i+1, i, token[i]);
        
        return 0;
    

 

10.字符串转数值函数(<stdlib.h>)

1、atoi函数:复习以下知识点后,练习 习题13,自己实现atoi()函数。

  1. 函数原型:int   atoi(const   char  *str);
  2. 操作数:①字符串的首地址str。
  3. 函数功能:在字符串str中逐字符读取,直到读到的字符不是①'-'(首次读取的'-'才有效,并且必须在字符串首位的才有效)和 ②'0' ~ '9' ,将读取到的字符转换成int型数据。读取失败返回0。
    #include <stdio.h>
    #include <stdlib.h>
    int main(int argc, char const *argv[])
    
        char *str1 = "0123abc";
        char *str2 = ".5f";
        char *str3 = "abc123.5";
        char *str4 = "-0123.5";
        printf("atoi(%s)=%d\\n", str1, atoi(str1));
        printf("atoi(%s)=%d\\n", str2, atoi(str2));
        printf("atoi(%s)=%d\\n", str3, atoi(str3));
        printf("atoi(%s)=%d\\n", str4, atoi(str4));
        return 0;
    

2、atof函数:复习以下知识点后,练习 习题14,自己实现atof()函数。

  1. 函数原型:double   atof(const   char  *str);
  2. 操作数:①字符串的首地址str。
  3. 函数功能:在字符串str中逐字符读取,直到读到的字符不是①'-'(首次读取的'-'才有效,并且必须在字符串首位的才有效)和 ②'0' ~ '9' 和③ '.'(首次读取的'.'才有效),将读取到的字符转换成double型数据。读取失败返回0。
    #include <stdio.h>
    #include <stdlib.h>
    int main(int argc, char const *argv[])
    
        char *str1 = "123abc";
        char *str2 = ".5f";
        char *str3 = "abc123.5";
        char *str4 = "-0123.5";
        printf("atof(%s)=%lf\\n", str1, atof(str1));
        printf("atof(%s)=%lf\\n", str2, atof(str2));
        printf("atof(%s)=%lf\\n", str3, atof(str3));
        printf("atof(%s)=%lf\\n", str4, atof(str4));
        return 0;
    

习题

习题5:试着实现strcat的函数功能

  1. 思路:
    f1. 封装实现strcat函数功能的API: char* my_strcat(char *dest, const char *src); 
    	f1.1 判断指针dest或者指针src的值是否是NULL
        	f1.1.1 如果是,
            		那么,代表函数执行失败,提前结束函数调用,返回NULL
        f1.2 定义一个备份指针ptr指向dest所指内容
    	f1.3 while循环,控制循环的变量是*dest,当指针dest没有偏移到末尾 时,进入循环
        //内在逻辑:先让dest指针自行偏移到字符数组的末尾(第一个'\\0')
    		f1.3.1 偏移字符指针dest的指向: dest++;
    	f1.4 while循环,控制循环的变量是*src,指针src没有偏移到末尾 时,进入循环
        //内在逻辑:从dest指向的字符数组的末尾(第一个'\\0')开始复制src
        	f1.4.1 通过指针间接改变dest字符数组中的字符,将src中的字符复制到dest对应位置:
    				*dest = *src;
            f1.4.2 偏移字符指针str的指向: str++;
    		f1.4.3 偏移字符指针dest的指向: dest++;
    	f1.5 通过指针间接修改,令拷贝过来的最后一位是空字符'\\0': *dest = '\\0';
    	f1.6 返回ptr的值
    1. 初始化或者输入一个字符串,只可以放在栈区、堆区,比如: char str[20] = "abcde";
    2. 调用API1. 将某个字符串拼接到str的末尾,比如: my_strcat(str, "hello");
    3. 打印拼接前后的字符串

  2. 代码:
    #include <stdio.h>
    char* my_strcat(char *dest, const char *src);
    int main(int argc, char const *argv[])
    
        char str[20] = "abcde";
        char *src = "hello";
        printf("拼接前:%s\\n", str);
        my_strcat(str, src);
        printf("拼接后:%s\\n", str);
        return 0;
    
    char* my_strcat(char *dest, const char *src)
    
        if(dest==NULL || src==NULL)
            return NULL;
        
        char *ptr = dest;
        while(*dest != '\\0')
            dest++;
        
        while(*src != '\\0')
            *dest = *src;
            dest++;
            src++;
        
        *dest = '\\0';
        return ptr;
    

 

习题6:试着实现strncat的函数功能

  1. 思路:
    f1. 封装实现strcat函数功能的API: char* my_strncat(char *dest, const char *src, int count); 
    	f1.1 判断指针dest或者指针src的值是否是NULL
        	f1.1.1 如果是,
            		那么,代表函数执行失败,提前结束函数调用,返回NULL
        f1.2 定义一个备份指针ptr指向dest所指内容
    	f1.3 while循环,控制循环的变量是*dest,指针dest没有偏移到末尾时,进入循环
        //内在逻辑:先让dest指针自行偏移到字符数组的末尾(第一个'\\0')
    		f1.3.1 偏移字符指针dest的指向: dest++;
    	f1.4 while循环,控制循环的变量是*src和count,当指针src没有偏移到末尾 并且count>0 时,进入循环
        //内在逻辑:不妨先假设count<src所指字符串长度,这时条件count>0先不满足
        //那么,就从dest指向的字符数组的末尾(第一个'\\0')开始复制src
        	f1.4.1 通过指针间接改变dest字符数组中的字符,将src中的字符复制到dest对应位置:
    				*dest = *src;
            f1.4.2 偏移字符指针str的指向: str++;
    		f1.4.3 偏移字符指针dest的指向: dest++;
    		f1.4.4 修改循环变量count: count--;
    	f1.5 判断count是否大于0
        //内在逻辑:条件*src!='\\0'先不满足,说明count>src所指字符串长度,那么让多拼接的部分用'\\0'填充
        	f1.5.1 如果是,那么说明count>src所指字符串长度
        		f1.5.1.1 while循环,循环变量count,当count>0 时,进入循环
            		f1.5.1.1.1 通过指针间接修改: *dest = '\\0';
    				f1.5.1.1.2 偏移字符指针dest的指向: dest++;
    				f1.5.1.1.3 修改循环变量count: count--;
    	f1.6 返回ptr的值
    1. 初始化或者输入一个字符串,只可以放在栈区、堆区,比如: char str[20] = "abcde";
    2. 调用API1. 将某个字符串的前几个字节拼接到str末尾,比如: my_strncat(str, "hello", 2);
    3. 打印拼接前后的字符串

  2. 代码:
    #include <stdio.h>
    char* my_strncat(char *dest, const char *src, int count);
    int main(int argc, char const *argv[])
    
        char str[20] = "abcde";
        char *src = "hello";
        printf("拼接前:%s\\n", str);
        my_strncat(str, src, 2);
        printf("拼接前2个字节后:%s\\n", str);
        return 0;
    
    char* my_strncat(char *dest, const char *src, int count)
    
        if(dest==NULL || src==NULL)
            return NULL;
        
        char *ptr = dest;
        while(*dest != '\\0')
            dest++;
        
        while(count>0 && *src!='\\0')
            *dest = *src;
            dest++;
            src++;
            count--;
        
        if(count > 0)
            while(count > 0)
                *dest = '\\0';
                dest++;
                count--;
            
        
        return ptr;
    

 

习题7:试着实现strcmp的函数功能

  1. 思路:                                      
    f1. 封装实现strcmp函数功能的API: int my_strcmp(const char *str1, const char *str2); 
    	f1.1 判断指针dest或者指针src的值是否是NULL
        	f1.1.1 如果是,
            		那么,代表函数执行失败,提前结束函数调用,返回2(打个比方)
        f1.2 定义结果变量ret,不妨先假设两个字符串相等,即: int ret = 0;
    	f1.3 while循环,控制循环的变量是*str1和*str2,当指针str1和str2都没有偏移到末尾 时,进入循环
        //内在逻辑:不对字符串长度进行判断,直接在两个字符串的有效长度内进行逐字符的ascii码大小比较
        	f1.3.1 判断*str1是否大于*str2
            	f1.3.1.1 如果是,
                	f1.3.1.1.1 那么结果就是str1>str2,直接返回1,提前结束函数: return 1;
            	f1.3.1.2 否则,判断*str1是否小于*str2
                	f1.3.1.2.1 如果是,
                    	f1.3.1.2.1.1 那么结果就是str1<str2,直接返回-1,提前结束函数: return -1;
            	f1.3.1.3 否则,
            		f1.3.1.3.1 偏移字符指针str1的指向: str1++;
    				f1.3.1.3.2 偏移字符指针str2的指向: str2++;
    	f1.4 判断指针str1是否没有偏移到字符串末尾,即*str1 !='\\0'
        //内在逻辑:如果上述循环一直正常结束,那么肯定有一个字符串读到了'\\0',因此
        //不妨假设str1的长度比str2的长,这时条件*str2!='\\0'先不满足
        	f1.4.1 如果是,
            		   那么不用往下比较了,str1肯定大于str2,令: ret = 1;
        	f1.4.2 否则,判断指针str2是否没有偏移到字符串末尾,即*str2 !='\\0'
    			f1.4.2.1 如果是,
            				那么不用往下比较了,str1肯定小于str2,令: ret = -1;
    	f1.6 返回ret的值
    1. 初始化或者输入两个字符串,可以放在栈区、堆区、常量区,比如: 
    	char *str1 = "abcdechen";
    	char *str2 = "abcdechem";
    2. 调用API1. 比较str1和str2,结果保存在变量ret中: ret = my_strcmp(str1, str2);
    3. 打印ret的值

  2. 代码:
    #include <stdio.h>
    int my_strcmp(const char *str1, const char *str2);
    int main(int argc, char const *argv[])
    
        char *str1 = "abcd2chen3";
        char *str2 = "abcdechem3";
        int ret = my_strcmp(str1, str2);
        printf("RET=%d\\n", ret);
        if(ret == 0)
            printf("两个字符串一样\\n");
        else
            printf("两个字符串不一样");
        
        return 0;
    
    int my_strcmp(const char *str1, const char *str2)
    
        if(str1==NULL || str2==NULL)
            printf("无效字符串\\n");
            return 2;
        
        int ret = 0;
        while(*str1!='\\0' && *str2!='\\0')
            if(*str1 > *str2)
                return 1;
            else if(*str1 < *str2)
                return -1;
            else
                str1++;
                str2++; 
            
        
        if(*str1!='\\0')
            ret = 1;
        else if(*str2!='\\0')
            ret = -1;
        
        return ret;
    

 

习题8:试着实现strncmp的函数功能。

  1. 思路:
    f1. 封装实现strncmp函数功能的API: 
    int my_strncmp(const char *str1, const char *str2, int count); 
    	f1.1 判断指针dest或者指针src的值是否是NULL
        	f1.1.1 如果是,
            		那么,代表函数执行失败,提前结束函数调用,返回2(打个比方)
        f1.2 定义结果变量ret,不妨先假设两个字符串相等,即: int ret = 0;
    	f1.3 while循环,控制循环的变量是*str1、*str2、count,
        当指针str1和str2都没有偏移到末尾 并且count>0 时,进入循环
        //内在逻辑:不对字符串长度进行判断,直接在两个字符串的有效长度内进行逐字符的ascii码大小比较
        //同时假设count是小于这两个字符串的有效长度的
        	f1.3.1 判断*str1是否大于*str2
            	f1.3.1.1 如果是,
                	f1.3.1.1.1 那么结果就是str1>str2,直接返回1,提前结束函数: return 1;
            	f1.3.1.2 否则,判断*str1是否小于*str2
                	f1.3.1.2.1 如果是,
                    	f1.3.1.2.1.1 那么结果就是str1<str2,直接返回-1,提前结束函数: return -1;
            	f1.3.1.3 否则,
            		f1.3.1.3.1 偏移字符指针str1的指向: str1++;
    				f1.3.1.3.2 偏移字符指针str2的指向: str2++;
    				f1.3.1.3.3 修改循环变量count: count--;
    	f1.4 判断count是否仍然大于0
        //内在逻辑:如果count>0,那么str1和str2两个指针必定有一个读到结束符'\\0'了
    		f1.4.1 判断指针str1是否没有偏移到字符串末尾,即*str1 !='\\0'
        	//内在逻辑:不妨假设str1的长度比str2的长,这时条件*str2!='\\0'先不满足
        		f1.4.1.1 如果是,
            				那么不用往下比较了,str1肯定大于str2,令: ret = 1;
        		f1.4.1.2 否则,判断指针str2是否没有偏移到字符串末尾,即*str2 !='\\0'
    				f1.4.1.2.1 如果是,
            					那么不用往下比较了,str1肯定小于str2,令: ret = -1;
    	f1.6 返回ret的值
    1. 初始化或者输入两个字符串,可以放在栈区、堆区、常量区,比如: 
        char *str1 = "abcd2chen3";
        char *str2 = "abcdeche13567";
    2. 调用API1. 比较str1和str2的前几个字节,结果保存在变量ret中: ret = my_strcmp(str1, str2, 10);
    3. 打印ret的值

  2. 代码:
    #include <stdio.h>
    int my_strncmp(const char *str1, const char *str2, int count);
    int main(int argc, char const *argv[])
    
        char *str1 = "abcd2chen3";
        char *str2 = "abcdeche13567";
        int ret = my_strncmp(str1, str2, 10);
        printf("RET=%d\\n", ret);
        if(ret == 0)
            printf("两个字符串的前10个字符一样\\n");
        else
            printf("两个字符串的前10个字符不一样");
        
        return 0;
    
    int my_strncmp(const char *str1, const char *str2, int count)
    
        if(str1==NULL || str2==NULL)
            printf("无效字符串\\n");
            return 2;
        
        int ret = 0;
        while(*str1!='\\0' && *str2!='\\0' && count>0)
            if(*str1 > *str2)
                return 1;
            else if(*str1 < *str2)
                return -1;
            else
                str1++;
                str2++;
                count--; 
            
        
        if(count > 0)
            if(*str1!='\\0') 
                ret = 1;
            else if(*str2!='\\0')
                ret = -1;
            
        
        return ret;
    

 

习题9:统计字符串中某个字符的个数和具体位置

  1. 思路:需要对字符串有深刻认识,字符串都能看作字符数组,某个字符串中的任意字符到末尾的空字符'\\0'都能形成一个字符串(一个末尾为字符'\\0'的字符数组)。
    1. 初始化或输入一个字符串,可以放在栈区、堆区、常量区,比如: 
    	char *str = "abcde12345 abcde12345 abcde12345";
    2. 先调用strchr函数对str做一次字符查找,并将返回地址保持在字符指针p中,比如: 
    	char *p = strchr(str, '3');
    3. 紧接着判断查找的字符是否存在,如果不存在就结束程序
    4. while循环,控制循环遍的变量是代表字符地址的p,当p != NULL 时,进入循环
    	4.1 修改用于记录字符'3'总数的变量num: num++; //不要忘了初始化为0
        4.2 打印第num个字符'3'距离字符串str的首地址多少字节数
        4.3 修改代表查找字符地址的循环遍变量p,让它指向str中第num个字符'3'后一个字节: p++;
    	4.4 修改代表查找字符地址的循环遍变量p,让它指向下一个字符'3'的地址:
    		p = strchr(p, '3');
    5. 打印字符'3'的总数num

  2. 代码:
    #include <stdio.h>
    #include <string.h>
    int main(int argc, char const *argv[])
    
        char *str = "abcde12345 abcde12345 abcde12345";
        int num = 0;
        char *p = strchr(str, '3');
        if(p == NULL)
            printf("找不到字符'3'");
            return 0;
        
        while(p != NULL)
            num++;
            printf("第%d个字符'3'距离字符串首地址%d个字节\\n", num, p-str);
            p++;
            p = strchr(p, '3');
        
        printf("共有%d个字符'3'", num);
        return 0;
    

习题10:试着实现strchr的函数功能

  1. 思路:
    f1. 封装实现strchr函数功能的API: char* my_strchr(char *str, int c); 
    	f1.1 判断指针str的值是否是NULL
        	f1.1.1 如果是,
            		那么,代表函数执行失败,提前结束函数调用,返回NULL
        f1.2 while循环,死循环
        	f1.2.1 判断当前指针str是否偏移到末尾
            	f1.2.1.1 如果是,
                			那么,断定没有找到字符c,提前结束函数调用,返回NULL
                f1.2.1.2 否则,判断当前指针str指向的内容是否等于字符c
                	f1.2.1.2.1 如果是,
                    				那么,代表找到字符c,提前结束函数调用,返回str
            	f1.2.1.3 否则,修改循环变量str的值,让指针str指向下一个字符: str++;
    			//内在逻辑:当str既没有偏移到末尾,有没有匹配上时,就让str自加,继续进行匹配
    1. 初始化或者输入一个字符串,可以放在栈区、堆区、常量区,比如: 
    	char *str = "abcde12345 abcde12345 abcde12345";
    2. 调用API1. 在字符串str中找字符,首地址保持在字符指针p中,比如: 
    	char *p = my_strchr(str, '3');
    3. 紧接着判断查找的字符是否存在,如果不存在就结束程序
    4. 打印第一个字符'3'距离字符串str首地址多少字节

  2. 代码:
    #include <stdio.h>
    char* my_strchr(char *str, int c);
    int main(int argc, char const *argv[])
    
        char *str = "abcde12345 abcde12345 abcde12345";
        char *p = my_strchr(str, '3');
        if(p == NULL)
            printf("找不到字符'3'");
            return 0;
        
        printf("第一个字符'3'距离字符串首地址%d个字节", p - str);
        return 0;
    
    char* my_strchr(char *str, int c)
    
        if(str == NULL)
            printf("字符串无效\\n");
            return NULL;
        
        while(1)
            if(*str == '\\0')
                return NULL;
            else if(*str == c)
                return str;
            
            str++;
        
    

习题11:试着实现strstr的函数功能

  1. 思路:循环嵌套、goto语句
    f1. 封装实现strstr函数功能的API: char* my_strstr(char *str, char *substr);
    	f1.1 判断指针str的值是否是NULL
        	f1.1.1 如果是,
            		那么,代表函数执行失败,提前结束函数调用,返回NULL
        f1.2 备份子字符串的首地址给字符指针ptr: char *ptr = substr;
    	//内在逻辑:我们的第二层循环是去让substr与str做匹配,如果中途失败,需要让指针substr复位
        f1.3 while循环,控制循环的变量是*str, 当指针str没有偏移到末尾 时,进入循环
        	f1.3.1 设定一个back标签: back:
    		//内在逻辑:每当第二层循环中substr与str不匹配,我们需要回到此处,继续执行以下相同代码
        	f1.3.2 判断当前str所指字符是否与当前substr所指字符相等
            	f1.3.2.1 如果是, //str和substr的头对上了,开始进入二层循环进行匹配
                	f1.3.2.1.1 定义一个复合语句中的局部字符指针变量p来记住当前str位置: char *p=str;
    				f1.3.2.1.2 while循环,控制循环的变量是*substr, 当指针substr没有偏移到末尾 时,进入循环
                    	f1.3.2.1.2.1 判断当前str所指字符是否与当前substr所指字符相等
                        	f1.3.2.1.2.1.1 如果是,
                            	f1.3.2.1.2.1.1.1 修改循环变量str的值,让指针str指向下一个字符: str++;
    							f1.3.2.1.2.1.1.2 修改循环变量substr的值,让指针substr指向下一个字符: substr++;
    						f1.3.2.1.2.1.2 否则,// 意味着不得不退出二层循环,终止匹配
                            	f1.3.2.1.2.1.2.1 让指针substr复位: substr = ptr; //5行
    							f1.3.2.1.2.1.2.2 用goto语句回跳到一层循环的开始: goto back: //8行
    				f1.3.2.1.3 提前结束函数调用,返回字符指针p: return p; //12行
    				//内在逻辑:顺利通过二层循环,说明substr已经和str匹配上了,返回起始匹配位置
                f1.3.2.2 否则,
                    f1.3.2.2.1 修改循环变量str的值,让指针str指向下一个字符: str++;
        f1.4 结束函数调用,返回NULL
        //内在逻辑:顺利通过一层循环,说明str偏移到末尾还没有和substr匹配上,故返回NULL表示没找到
    1. 初始化或者输入两个字符串,可以放在栈区、堆区、常量区,比如: 
    	char *str = "abcde1234$#$ abcde5678$#$";
    	char *substr = "$#$";
    2. 调用API1. 在字符串str中找子串substr,首地址保持在字符指针p中,比如: 
    	char *p = my_strstr(str, substr);
    3. 紧接着判断查找的子字符串是否存在,如果不存在就结束程序
    4. 打印第一个子字符串substr的首地址距离字符串str的首地址多少个字节

  2. 代码:
    #include <stdio.h>
    char* my_strstr(char *str, char *substr);
    int main(int argc, char const *argv[])
    
        char *str = "abcde1234$#$ abcde5678$#$";
        char *substr = "$#$";
        char *p = my_strstr(str, substr);
        if(p == NULL)
            printf("找不到子字符串\\n");
            return 0;
        
        printf("第一个子字符串%s距离字符串首地址%d个字节", substr, p-str);
        return 0;
    
    char* my_strstr(char *str, char *substr)
    
        if(str==NULL || substr==NULL)
            printf("无效字符串\\n");
            return NULL;
        
        char *ptr = substr;
        while(*str != '\\0')
            back:
            if(*str == *substr)
                char *p = str;
                while(*substr != '\\0')
                    if(*str == *substr)
                        str++;
                        substr++;
                    
                    substr = ptr;
                    goto back;
                
                return p;
            
            str++;
        
        return NULL;
    

 

习题12:从手机短信格式中解析信息 "+CMGR:REC UNREAD,+8613857607035,23/01/03,19:31:24+00,你好吗?",写成一个函数。

  1. 思路:通过二级指针引用普通指针数组
    f1. 封装解析短信信息的API: int msg_deal(char *msg_src, char *delim, char **msg_done); 
     形参是短信来源字符串、分隔符、调用字符指针数组的二级指针msg_done
    	f1.1 调用strtok函数,用delim分割msg_src,获取首个子字符串首地址,保存在字符指针数组第0个元素:
    		int i = 0;	//i是字符指针数组的下标
    		msg_done[i] = strtok(msg_src, delim);
    	f1.2 while循环,控制循环的变量是分割到的子字符串首地址msg_done[i],地址不为NULL 时,进入循环
        	f1.2.1 修改代表字符指针数组下标的循环变量i的值: i++;
    		f1.2.2 调用strtok函数,从msg_src中分割子字符串,子串的首地址保存在字符指针数组第i个元素:
    			msg_done[i] = strtok(NULL, delim);
    	f1.3 返回i的值,因为我们顺便用i记录子字符串的个数
    1. 初始化或输入一个字符串,代表短信来源字符串,只可以放在栈区、堆区,比如: 
    	char msg_src[100] = "+CMGR:REC UNREAD,+8613857607035,23/01/03,19:31:24+00,你好吗?";
    2. 初始化一个字符串,代表分隔符,可以放在栈区、堆区、常量区,这里规定用逗号分割:
    	char *delim = ",";
    3. 定义一个字符指针数组,一会用来存放分割来的字符串的首地址: char *p[6];
    4. 调用API1. 将msg_src用delim分割,字符串首地址保存在字符指针数组中: 
    	msg_deal(msg_src, delim, p);
    5. 打印字符串个数num,以及短信信息内容。

  2. 代码:
    #include <stdio.h>
    #include <string.h>
    int msg_deal(char *msg_src, char *delim, char **msg_done);
    int main(int argc, char const *argv[])
    
        char msg_src[100] = \\
        "+CMGR:REC UNREAD,+8613857607035,23/01/03,19:31:24+00,你好吗?";
        char *delim = ",";
        char* p[6];
        int num;
        num = msg_deal(msg_src, delim, p);    
        printf("字符串个数=%d\\n", num);
        printf("状态  :%s\\n", p[0]+10);
        printf("手机号:%s\\n", p[1]+3);
        printf("日期  :%s\\n", p[2]);
        *(p[3] + 8) = '\\0';
        printf("时间  :%s\\n", p[3]);
        printf("内容  :%s\\n", p[4]);
        return 0;
    
    int msg_deal(char *msg_src, char *delim, char **msg_done)
    
        int i = 0;
        msg_done[i] = strtok(msg_src, delim);
        while (msg_done[i] != NULL)
            i++;
            msg_done[i] = strtok(NULL, delim);
        
        return i;
    

习题13:试着实现atoi的函数功能

  1. 思路:
    f1. 封装实现atoi功能的函数: int my_atoi(const char *str);
    	f1.1 判断str字符串的首个字符是否是'-',
        	f1.1.1 如果是,
            	f1.1.1.1 令记录数值正负号的变量flag为-1: flag = -1; // 一开始定义成 int flag = 1;
                f1.1.1.2 让指针str指向下一个字符: str++;
    	f1.2 while循环,控制循环的变量是*str,当指针str没有偏移到末尾 时,进入循环
        	f1.2.1 判断指针str当前指向的字符是否是'0'或'1'或'2'或'3'或'4'或'5'或'6'或'7'或'8'或'9'
            	f1.2.1.1 如果是,
                	f1.2.1.1.1 修改保存整型数值的变量temp,它等于自身×10,再加上本次读取数字:
    						temp = temp*10 + (*str-'0');//temp一开始初始化成0,万一找不到就返回0
    				f1.2.1.1.2 让指针str指向下一个字符: str++;
    			f1.2.1.2 否则,
                			那么说明碰到别的字符,需要用break提前退出循环
    	f1.3 通过flag判断整型数是否是负数,
        	f1.3.1 如果是,
            		那么,返回 (-1)*temp;
    		f1.3.2 否则,
            		返回 temp;

  2. 代码:
    #include <stdio.h>
    int my_atoi(const char *str);
    int main(int argc, char const *argv[])
    
        printf("%d\\n", my_atoi("-01234.a"));
        return 0;
    
    int my_atoi(const char *str)
    
        int flag = 1;
        int temp = 0;
        if (*str == '-')
            flag = -1;
            str++;
            
        while (*str != '\\0')
            if(*str == '0' || *str == '1' || \\
               *str == '2' || *str == '3' || \\
               *str == '4' || *str == '5' || \\
               *str == '6' || *str == '7' || \\
               *str == '8' || *str == '9')
            
                temp = temp*10 + (*str - '0');
                str++;
            else
                break;
            
        
        if (flag == -1)
            return (-1)*temp;
        else
            return temp;
        
    

习题14:试着实现atof的函数功能

  1. 思路:多分支选择控制语句(条件制约)
    f1. 封装实现atof功能的函数: double my_atof(const char *str);
    	f1.1 判断str字符串的首个字符是否是'-',
        	f1.1.1 如果是,
            	f1.1.1.1 令记录数值正负号的变量flag为-1: flag = -1; // 一开始定义成 int flag = 1;
                f1.1.1.2 让指针str指向下一个字符: str++;
    	f1.2 while循环,控制循环的变量是*str,当指针str没有偏移到末尾 时,进入循环
        	f1.2.1 判断指针str当前指向的字符是否是'0'或'1'或'2'或'3'或'4'或'5'或'6'或'7'或'8'或'9'
            并且,小数点符号'.'是否没有出现过,在此之前需要定义:
            		1、代表表示小数点出现次数的变量: int periodOccur=0;
            	f1.2.1.1 如果是,我们开始处理整数部分
                	f1.2.1.1.1 修改保存整型部分数值的变量intPart,它等于自身×10,再加上本次读取数字:
    						intPart = intPart*10 + (*str-'0');	//intPart一开始初始化成0
    				f1.2.1.1.2 让指针str指向下一个字符: str++;
    			f1.2.1.2 否则,判断str当前指向的字符是否是小数点符合'.',并且'.'是否没有出现过
                	f1.2.1.2.1 如果是,
                    //内在逻辑:我们通过变量periodOccur制约分支条件1.2.1和1.2.1.2,第一个'.'有效
                    	f1.2.1.2.1.1 修改代表小数点'.'出现次数的变量: periodOccur = 1;
    					f1.2.1.2.1.2 让指针str指向下一个字符: str++;
    			f1.2.1.3 否则,判断指针str当前指向的字符是否是'0' ~ '9'
                    f1.2.1.3.1 如果是,我们开始处理小数部分,在此之前,需要定义:
    						1、代表小数数值的变量: double fracPart;
                        	2、代表当前小数点后位数的变量: int decimalPlace;
    					    3、根据当前位数,使用循环确定读取到的小数数值的循环变量: double currentFrac;
                    	f1.2.1.3.1.1 设置循环入口: currentFrac = (*str - '0');
    					f1.2.1.3.1.2 for循环,代表循环次数的循环变量i从0开始, <decimalPlace 时,进入循环
                        	f1.2.1.3.1.2.1 修改循环变量currentFrac: currentFrac = currentFrc / 10;
    					f1.2.1.3.1.3 修改保存小数部分数值的变量fracPart: fracPart += currentFrac;
    					f1.2.1.3.1.4 修改代表当前小数位数的变量: decimalPlace++;
    					f1.2.1.3.1.5 让指针str指向下一个字符: str++;
    			f1.2.1.4 否则,那么说明碰到别的字符,需要用break提前退出循环
    	f1.3 通过flag判断结果是否是负数,
        	f1.3.1 如果是,
            		那么,返回 (-1)*(intPart+fracPart);
    		f1.3.2 否则,
            		返回 (intPart+fracPart);

  2. 代码:
    #include <stdio.h>
    double my_atof(const char *str);
    int main(int argc, char const *argv[])
    
        printf("%lf\\n", my_atof("-01234.24a"));
        return 0;
    
    double my_atof(const char *str)
    
        int flag = 1;
        int intPart = 0;
        double fracPart = 0;
        int periodOccur = 0;
        int decimalPlace = 1;
        double currentFrac = 0;
        if (*str == '-')
            flag = -1;
            str++;
            
        while (*str != '\\0')
            if( (*str == '0' || *str == '1' || \\
                 *str == '2' || *str == '3' || \\
                 *str == '4' || *str == '5' || \\
                 *str == '6' || *str == '7' || \\
                 *str == '8' || *str == '9') && periodOccur==0 ) //整数部分处理
            
                intPart = intPart*10 + (*str - '0');
                str++;
            else if(*str=='.' && periodOccur==0)              //小数部分处理
                periodOccur = 1;
                str++;
            else if(*str == '0' || *str == '1' || \\
                     *str == '2' || *str == '3' || \\
                     *str == '4' || *str == '5' || \\
                     *str == '6' || *str == '7' || \\
                     *str == '8' || *str == '9')
            
                currentFrac = (*str - '0');
                for(int i=0; i<decimalPlace; i++)
                    currentFrac = currentFrac / 10;
                
                fracPart = fracPart + currentFrac;
                decimalPlace++;
                str++;
            else
                break;
            
        
        if (flag == -1)    
            return (-1)*(intPart+fracPart);
        else
            return (intPart+fracPart);
        
    

C的实用笔记36——几种常用的字符串处理API

0、const关键字

1、知识点const是与存储相关的关键字,用作常量声明 ,修饰普通变量和指针变量,表示只读
  1. const修饰普通变量,修饰后变量从可修改的左值变成不可修改的左值 
  2. const修饰指针变量:分为三种情况。
    1. 指针指向的内容是只读的(常量指针):。虽然不能通过指针变量str修改其指向的内容(比如指针偏移法、指针下标法、指针自加法),但不能保证没有别的指针指向该内存然后进行修改。
    2. 指针本身是只读的(指针常量,或称地址常量):。str指向的内存的内容,是有可能通过str来进行修改的(前提是str指向的内存的内容是可以修改的),比方说数组名就是地址常量
    3. 指针本身以及指向的内容都是只读的:

2、一些变量名含义:

  1. format:格式字符串
  2. buffer:缓冲区字符串
  3. delim:划分字符串
  4. substr:子字符串

1.输出字符串函数(<stdio.h>)

1、puts函数:

  1. 函数原型:int   puts(const  char   *str);
  2. 操作数:①字符串的首地址。
  3. 函数功能:以只读的方式接收一个字符串,打印在屏幕(标准输出:stdout)上并换行。
    puts("请输入一个字符串");

2、printf函数:

  1. 函数原型:int   printf(const  char   *format,........);
  2. 操作数:①格式字符串format,它里面包括占位符和原样输出两部分。
  3. 函数功能:将格式字符串format里的占位符替换成对应数据,同时保留原样输出部分,最后打印在屏幕上
    char *str = "abcde";
    printf("%s", str);

3、sprintf函数:

  1. 函数原型:int   sprintf(char  *dest,   const  char  *format,..........);
  2. 操作数:①字符型指针dest是字符数组。
  3. 函数功能:将格式字符串format里的占位符替换成对应数据,同时保留原样输出部分,最后复制到dest指向的字符数组中。
  4. 说明:sprintf()函数和printf()函数唯一的区别在于字符串打印位置从屏幕变成了字符数组,因此sprintf()比printf()的形参多了一个。
    #include <stdio.h>
    int main(int argc, char const *argv[])
    
        char str[10];
        sprintf(str, "%d:%d:%d", 2023, 3, 29);
        puts(str);
        return 0;
      

2.获取字符串函数(<stdio.h>)

0、知识点:

  1. 内存污染(存储字符串的目的内存不够用时,后面的内存会被污染,也就是被修改,类似于下标越界)

1、gets:

  1. 函数原型:char*   gets(char   *buffer);
  2. 操作数:①用于接收键盘缓冲区字符串的字符数组buffer。
  3. 函数功能:从键盘缓冲区中获取一个字符串,复制到buffer指向的字符数组中,然后返回这个字符串的首地址。
  4. 优点:①字符串输入时可以有空格,不像scanf函数中格式控制符%s碰到空格就会跳过。
  5. 缺点:gets函数没法像scanf那样控制输入字符串的长度。①易发生溢出(内存污染)。如果溢出,多出来的字符将被写入到堆栈中,这就覆盖了堆栈原先的内容,破坏一个或多个不相关变量的值。
    #include <stdio.h>
    int main(int argc, char const *argv[])
    
        char str[10] = '\\0';
        printf("请输入字符串(长度在9以内)\\n");
        gets(str);
        printf("%s\\n", str);
        return 0;
      

2、scanf函数:

  1. 函数原型:int   scanf(const  char  *format,..........);
  2. 操作数:①格式字符串format,它里面包括占位符和原样输入两部分。
  3. 函数功能:将格式字符串format里的占位符替换成键盘缓冲区中的数据,最后保存在用户指定的地址中。
  4. 优点:scanf能控制输入字符串的长度,如果用户输入溢出了,那么①多余的字符还会被保留在键盘缓冲区中而不是复制到堆栈里、②scanf用法更多,不仅能获取字符串,还能获取别的数据到指定地址中。
  5. 缺点:单一使用%s格式控制符,就无法识别键盘缓冲区中的空格,这需要用到scanf的高级用法。
    #include <stdio.h>
    int main(int argc, char const *argv[])
    
        char str[10] = '\\0';
        printf("请输入字符串(长度在9以内)\\n");
        scanf("%9s", str);
        printf("%s\\n", str);
        return 0;
      

3、sscanf函数:

  1. 函数原型:int   sscanf(const  char  *src,  const  char  *format,..........);
  2. 操作数:①字符指针src指向来源字符串,它就像scanf函数中从键盘缓冲区中获取的字符串;②格式字符串format,它里面包括占位符和原样输入两部分。
  3. 函数功能:将格式字符串format里的占位符替换成指针src指向的字符串中的对应数据,最后保存在用户指定的地址中。
  4. 说明:sscanf()函数和scanf()函数唯一的区别在于来源字符串从键盘缓冲区变成了字符指针指向的字符串,因此sscanf()比scanf()的形参多了一个。
    #include <stdio.h>
    int main(int argc, char const *argv[])
    
        char str1[20], str2[20];
        sscanf("abcd 1234", "%s %s", str1, str2);
        printf("str1=%s, str2=%s\\n", str1, str2);
        
        int year, month, day;
        sscanf("2023:3:29", "%d:%d:%d", &year, &month, &day);
        printf("year=%d,month=%d,day=%d", year, month, day);
        return 0;
      

4、scanf和sscanf的高级用法:先看以下知识点,然后再看习题1,巩固sscanf用法。

  1. 在占位符前面加*,来跳过来源字符串中的某个数据:
    #include <stdio.h>
    int main(int argc, char const *argv[])
    
        char str[20] = '\\0';
        sscanf("1234 5678", "%*d%s", str);		//%*d表示读取到一个整型数据但是跳过他
        printf("str = %s\\n", str);
        puts("请输入");
        scanf("%*d%s", str);					//%*d表示读取到一个整型数据但是跳过他
        printf("str = %s\\n", str);
        return 0;
      

  2. %s的一种特殊写法:%[ ] ,表示获取字符串时只要中括号里的指定字符,碰到其他字符就结束获取:中括号里的字符有两种书写方式:①%[a-z],用字符'-'连接,表示以ASCII码为顺序从a到z中的所有字符;②%[aBc],列举出所有字符。当字符多且范围不连续时,不能连用:%[ ]%[ ](原因是会认为你要输入两个字符串),要写在一个中括号里,比如%[a-zA-Z]表示只要字母
    #include <stdio.h>
    int main(int argc, char const *argv[])
    
        char str[20] = '\\0';
        sscanf("abcdeABCDE,1234FGH", "%[a-zA-Z]", str);	//只要字母
        puts(str);
        printf("请输入字符串\\n");
        scanf("%[a-zA-Z]", str);						//只要字母
        puts(str);
        return 0;
      

  3. %s的一种特殊写法:%[^ ] ,表示获取字符串时就不要中括号里的指定字符,碰到这些字符就结束获取:同理,中括号里的字符有两种书写方式:①%[a-z],用字符'-'连接,表示以ASCII码为顺序从a到z中的所有字符;②%[aBc],列举出所有字符。当字符多且范围不连续时,不能连用:%[ ]%[ ](原因是会认为你要输入两个字符串),比如%[^a-z0-9]表示就不要小写字母和数字
    #include <stdio.h>
    int main(int argc, char const *argv[])
    
        char str[20] = '\\0';
        sscanf("ABCDE1234abcde", "%[^a-z0-9]", str);	//就不要小写字母和数字
        puts(str);
        printf("请输入字符串\\n");
        scanf("%[^a-z0-9]", str);
        puts(str);
        return 0;
      

5、输入字符串时允许有多个空格的方法:

  1. scanf("%[^\\n]",str);  通常来说,我们以%[^ ]这种方式规定字符串结束读取的条件,比如%[^\\n]就规定了以回车符作为字符串读取结束的标志。缺点:输入一个带有空格的字符串,并按下回车后,字符'\\n'还在键盘缓冲区中,因此如果需要输入多个字符串,就得在两条输入语句中加一条getchar()语句。
    #include <stdio.h>
    int main(int argc, char const *argv[])
    
        char str[20] = '\\0';
        char str1[20] = '\\0';
        printf("请输入字符串\\n");
        scanf("%[^\\n]", str);		//规定以回车符作为字符串读取的结束标志
        puts(str);
        getchar();					//加getchar()来接收回车符
        printf("请输入字符串\\n");
        scanf("%[^\\n]", str1);		//规定以回车符作为字符串读取的结束标志
        puts(str1);
        return 0;
      

  2. scanf("%[^\\n]%*c",str);  连用%[^\\n] 和 %*c,由于输入完字符串后我们要按下回车,所以最后的换行符'\\n'其实也算在我们输入的字符串当中,而%*c的功能是在往指针str指向的内容写字符串时,跳过最后一个字符,相当于getchar()函数把换行符吃掉。
    #include <stdio.h>
    int main(int argc, char const *argv[])
    
        char str[20] = '\\0';
        char str1[20] = '\\0';
        sscanf("hello world\\n", "%[^\\n]%*c", str);		//规定以回车符作为字符串读取的结束标志,同时跳过最后一个回车符
        puts(str);
        sscanf("day day up\\n", "%[^\\n]%*c", str1);		//规定以回车符作为字符串读取的结束标志,同时跳过最后一个回车符
        puts(str1);
        return 0;
      

3.字符串长度计算函数

1、strlen函数:练习习题2,自己实现strlen()函数(嵌入式笔试)

  1. 函数原型:size_t   strlen(const   char  *str);
  2. 操作数:①字符串的首地址。
  3. 函数功能:以只读的方式接收一个字符串,计算它的有效字符长度。
    #include <stdio.h>
    #include <string.h>
    int main(int argc, char const *argv[])
    
        char str[20] = "abcde";
        size_t len;
        len = strlen(str);
        printf("%s的有效长度:%d\\n", str, len);
        return 0;
    

4.字符串拷贝函数

0、知识点:

  1. 内存污染(存储字符串的目的内存不够用时,后面的内存会被污染,也就是被修改,类似于下标越界)

1、strcpy函数:练习 习题3,自己实现strcpy()函数(嵌入式笔试)

  1. 函数原型:char*   strcpy(char  *dest, const   char  *src);
  2. 操作数:①目的字符数组的首地址dest;②来源字符串的首地址src;
  3. 函数功能:将字符指针src指向的字符串拷贝到dest所指的字符数组之中(src末尾的空字符'\\0'也会被复制,dest没被修改的部分保留原样),并返回被复制后的字符数组的首地址dest。
  4. 缺点:①当src所指字符串长度 > dest指向的内存大小时,字符串会溢出,造成内存污染。尽量避免。
    #include <stdio.h>
    #include <string.h>
    int main(int argc, char const *argv[])
    
        char str[20] = "1234567890123456789";
        char *src = "abcde";
        printf("拷贝前:%s\\n", str);
        strcpy(str, src);
        printf("拷贝后:%s\\n", str);
        printf("拷贝后没被修改的部分:%s", str+6);
        return 0;
    

2、strncpy函数: 练习 习题4,自己实现strncpy()函数(嵌入式笔试)

  1. 函数原型:char*   strncpy(char  *dest, const  char  *src,  size_t   count);
  2. 操作数:①目的字符数组的首地址dest;②来源字符串的首地址src;③限制拷贝的字节数count。
  3. 函数功能:将字符指针src所指向的字符串中以src地址开始的后count个字节复制到dest所指的数组中,并返回被复制后的dest,并返回被复制后的字符数组的首地址dest。
  4. 内部决策:当n大小 > src所指字符串长度时,多复制的部分会用空字符'\\0'填充。
  5. 缺点:①当n大小 > dest指向的内存大小时,字符串会溢出,造成内存污染。尽量避免。
    #include <stdio.h>
    #include <string.h>
    int main(int argc, char const *argv[])
    
        char str[20] = "1234567890123456789";
        char *src = "abcde";
        printf("拷贝前:%s\\n", str);
        strncpy(str, src, 2);
        printf("拷贝前2个字节后:%s\\n", str);
        return 0;
    

5.断言(<assert.h>)

1、assert宏定义:

  1. 由来:对于那些执行失败的指针函数,函数内部会先返回NULL,不再继续执行下面的内容,比如说习题4中一开始就要判断dest和src是否是NULL,如果是就终止函数并返回NULL。从这个例子出发,更一般的,我们会遇到一些判断语句(如果出错,就必须结束程序),然而这些出错事件发生的可能性比较小,算是小概率事件,如果需要判断的特殊情况过多(也就是if语句过多),或者说if语句内容过长,这十分不方便程序阅读。所以,为了方便我们判断小概率出错情况,C库宏定义了assert。
  2. 功能:如果assert中的条件返回错误(0),那么它先向 stderr (屏幕,标准错误)打印一条出错信息,然后通过调用 abort 函数来终止程序运行。
  3. 使用方法:assert(expression);  assert函数主要用在调试阶段,调试通过后,通常宏定义一个 #define NDEBUG 来禁用 assert 调用。
  4. 测试:                     
    #include <stdio.h>
    #include <assert.h>
    char* my_strncpy(char *dest, const char *src, int count);
    int main(int argc, char const *argv[])
    
        char str[20] = "1234567890123456789";
        printf("拷贝前:%s\\n", str);
        my_strncpy(str, NULL, 2);					//这里不妨做个测试,看看assert都打印了什么
        printf("拷贝前2个字节后:%s\\n", str);
        return 0;
    
    char* my_strncpy(char *dest, const char *src, int count)
    
        assert(dest!=NULL && src!=NULL);
    

 

习题

习题1:【从已知字符串中获取其中两个字符中间的内容】现有字符串"account:#123456789@qq.com",试获取#和@符号之间的字符串123456789。

  1. 思路:
    1. 使用sscanf从字符串"account:#123456789@qq.com"中以如下规则获取字符串输入到字符数组str中:
    	规则 1.1: 就不要符号'#'之前的内容, 同时将这部分字符给跳过,这里有点特别相当于%*s: %*[^#]
        规则 1.2: 把符号'#'作为原样输入,也可以把'#'写成"%*#"
        规则 1.3: 以符号'@'作为读取结束标志
    	综合以上规则,可以写出sscanf的format部分:"%*[^#]#%[^@]"  或者  "%*[^#]%*#%[^@]"
    2. 打印str,进行验证

  2. 代码:
    #include <stdio.h>
    int main(int argc, char const *argv[])
    
        char str[20] = '\\0';
        char *p = "account:#123456789@qq.com";
        sscanf(p, "%*[^#]#%[^@]", str);
        printf("str=%s\\n", str);
        return 0;
    

习题2:试着实现strlen的函数功能

  1. 思路:
    f1. 封装实现strlen函数功能的API: size_t my_strlen(cosnt char *str);  
    	f1.1 while循环,控制循环的变量是*str,也就是字符串str中的某个字符,当*str!='\\0' 时,进入循环
        	f1.1.1 偏移字符指针str的指向: str++;
    		f1.1.2 修改代表有效字符长度的变量len: len++; //记得初始化len=0;
    1. 初始化或者输入一个字符串,可以放在栈区、堆区、常量区,比如: char str[20] = "helloworld";
    2. 调用API1. 获取字符串str的有效长度,保存变量len中: len = my_strlen(str);
    3. 打印len

  2. 代码:
    #include <stdio.h>
    size_t my_strlen(const char *str);
    int main(int argc, char const *argv[])
    
        char str[20] = "helloworld";
        size_t len;
        len = my_strlen(str);
        printf("%s的有效长度:%d\\n", str, len);
        return 0;
    
    size_t my_strlen(const char *str)
              
        size_t len = 0;
        while (*str != '\\0')
            str++;
            len++;
        
        return len;
    

习题3:试着实现strcpy的函数功能

  1. 思路:
    f1. 封装实现strcpy函数功能的API: char* my_strcpy(char *dest, const char *src); 
    	f1.1 判断指针dest或者指针src的值是否是NULL
        	f1.1.1 如果是,
            			那么,代表函数执行失败,提前结束函数调用,返回NULL
        f1.2 定义一个备份指针ptr指向dest所指内容
    	f1.3 while循环,控制循环的变量是*str,也就是字符串str中的某个字符,当*str!='\\0' 时,进入循环
        	f1.3.1 通过指针间接改变dest字符数组中的字符,将src中的字符复制到dest对应位置:
    				*dest = *src;
            f1.3.2 偏移字符指针str的指向: str++;
    		f1.3.3 偏移字符指针dest的指向: dest++;
    	f1.4 通过指针间接修改,令拷贝过来的最后一位是空字符'\\0': *dest = '\\0';
    	f1.5 返回ptr的值
    1. 初始化或者输入一个字符串,只可以放在栈区、堆区,比如: char str[20] = "1234567890123456789";
    2. 调用API1. 将某个字符串拷贝到str中,比如: my_strcpy(str, "abcde");
    3. 打印拷贝前后的字符串

  2. 代码:
    #include <stdio.h>
    char* my_strcpy(char *dest, const char *src);
    int main(int argc, char const *argv[])
    
        char str[20] = "1234567890123456789";
        printf("拷贝前:%s\\n", str); 
        my_strcpy(str, "abcde");
        printf("拷贝后:%s\\n", str);
        printf("拷贝后没被修改的部分:%s", str+6);
        return 0;
    
    char* my_strcpy(char *dest, const char *src)
    
        if(dest==NULL || src==NULL)
            return NULL;
        
        char *ptr = dest;
        while (*src != '\\0')
            *dest = *src;
            dest++;
            src++;
        
        *dest = '\\0';
        return ptr;
    

习题4:试着实现strncpy的函数功能

  1. 思路:
    f1. 封装实现strncpy函数功能的API: char* my_strncpy(char *dest, const char *src, int count); 
    	f1.1 判断指针dest或者指针src的值是否是NULL
        	f1.1.1 如果是,
            			那么,代表函数执行失败,提前结束函数调用,返回NULL
        f1.2 定义一个备份指针ptr指向dest所指内容
    	f1.3 while循环,控制循环的变量是*str以及count,当*str!='\\0' 并且 count>0 时,进入循环
        //内在逻辑:不妨先假设count<src所指字符串长度,这时条件count>0先不满足
        	f1.3.1 通过指针间接改变dest字符数组中的字符,将src中的字符复制到dest对应位置:
    				*dest = *src;
            f1.3.2 偏移字符指针str的指向: str++;
    		f1.3.3 偏移字符指针dest的指向: dest++;
    		f1.3.4 修改循环变量count: count--;
    	f1.4 判断count是否大于0
        //内在逻辑:条件*src!='\\0'先不满足,说明count>src所指字符串长度,那么让多余的部分用'\\0'填充
        	f1.4.1 while循环,循环变量count,当count>0 时,进入循环
            	f1.4.1.1 通过指针间接修改: *dest = '\\0';
    			f1.4.1.2 偏移字符指针dest的指向: dest++;
    			f1.4.1.3 修改循环变量count: count--;
    	f1.5 返回ptr的值
    1. 初始化或者输入一个字符串,只可以放在栈区、堆区,比如: char str[20] = "1234567890123456789";
    2. 调用API1. 将某个字符串的前几位拷贝到str中,比如: my_strcpy(str, "abcde", 2);
    3. 打印拷贝前后的字符串

  2. 代码:
    #include <stdio.h>
    char* my_strncpy(char *dest, const char *src, int count);
    int main(int argc, char const *argv[])
    
        char str[20] = "1234567890123456789";
        char *src = "abcde";
        printf("拷贝前:%s\\n", str);
        my_strncpy(str, src, 2);
        printf("拷贝前2个字节后:%s\\n", str);
        return 0;
    
    char* my_strncpy(char *dest, const char *src, int count)
    
        if(dest==NULL || src==NULL) 
            return NULL;
        
        char *ptr = dest;
        while (count>0 && *src!='\\0')  //当count<src所指字符串长度时,条件count>0先不满足
            *dest = *src;
            dest++;
            src++;
            count--;
        
        if(count > 0)                 //如果说条件*src!='\\0'先不满足,说明count>src所指字符串长度 
            while(count > 0)
                *dest = '\\0';
                dest++;
                count--;
            
        
        return ptr;
    

以上是关于C的实用笔记37——几种常用的字符串处理API的主要内容,如果未能解决你的问题,请参考以下文章

学习笔记实用类String的基本应用即常用方法

JS中常用的几种时间格式处理-笔记整理

C语言进阶学习笔记七程序执行+调试技巧(实用技巧篇)

简单实用!传感器3种常用算法处理(附代码)

将图像存储在 id3v2 框架 apic 中

6.3-全栈Java笔记:异常处理方法(上)