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.输出字符串函数()

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 
    int main(int argc, char const *argv[])
    {
        char str[10];
        sprintf(str, "%d:%d:%d", 2023, 3, 29);
        puts(str);
        return 0;
    }  

2.获取字符串函数()

0、知识点:

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

1、gets:

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

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. 测试:                     C的实用笔记36——几种常用的字符串处理API(一)_第1张图片
    #include 
    #include 
    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:#[email protected]",试获取#和@符号之间的字符串123456789。

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

  2. 代码:
    #include 
    int main(int argc, char const *argv[])
    {
        char str[20] = {'\0'};
        char *p = "account:#[email protected]";
        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 
    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 
    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 时,进入循环
        //内在逻辑:不妨先假设count0先不满足
        	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 
    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'){  //当count0先不满足
            *dest = *src;
            dest++;
            src++;
            count--;
        }
        if(count > 0){                 //如果说条件*src!='\0'先不满足,说明count>src所指字符串长度 
            while(count > 0){
                *dest = '\0';
                dest++;
                count--;
            }
        }
        return ptr;
    }

你可能感兴趣的:(c语言)