【C语言】字符和字符串函数(2)

在这里插入图片描述

文章目录

  • 一、strncpy函数的使用
  • 二、strncat函数的使用
  • 三、strncmp函数的使用
  • 四、strstr的使用和模拟实现
  • 五、strtok函数的使用
  • 六、strerr函数的使用

一、strncpy函数的使用

   我们之前学习的strcpy的作用是把源字符串拷贝到目标空间内,而且经过我们的模拟实现,我们也意识到它拷贝的时候会把目标空间的内容给替换了,我们可以来测试一下:
在这里插入图片描述
   可以看到,将arr2的内容拷贝到arr1中时,把arr1原本的内容替换了,那假设我们不想让它全部拷贝过来,只想拷贝一部分怎么办呢?
   这个时候就要引入我们带n的字符串函数,那个多出来的n就代表数量,是我们想拷贝字符串时,要拷贝的字符的个数
   而不带n的字符串函数和带n的字符串函数的根本区别是带n的字符串函数更灵活,可以指定拷贝的字符的个数,所以也叫带n的字符串函数为受限制的字符串函数,不带n的为不受限制的字符串函数
   接下来我们来看看本次讲到的strncpy函数的原型:

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

   它的参数与strcpy的区别就是多了最后一个参数,它的作用就是指定我们要拷贝的字符的个数
它的特点如下:

  • 拷⻉num个字符从源字符串到⽬标空间
  • 如果源字符串的⻓度⼩于num,则拷⻉完源字符串之后,在⽬标的后边追加\0,直到num个

   接下来我们来使用一下它,使拷贝时只拷贝arr2数组中的前3个字符,如下:

#include <stdio.h>
#include <string.h>int main()
{char arr1[20] = "abcdef";char arr2[20] = "ghijklmn";strncpy(arr1, arr2, 3);printf("%s\n", arr1);return 0;
}

运行结果:
在这里插入图片描述
   可以看到只拷贝了arr2中的前三个字符进arr1,并且覆盖了arr1的前三个字符,这就是strncpy的作用
   至于strncpy的模拟实现,可以自行完成,与strcpy的模拟实现相似,这里就不再赘述

二、strncat函数的使用

   strncat也是带n家族的一员,多n的原因和strncpy差不多,就是用来指定要追加到目标空间的字符的个数,它的原型如下:

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

它的特点是:

  • 将source指向字符串的前num个字符追加到destination指向的字符串末尾,再追加⼀个 \0 字符
  • 如果source指向的字符串的字符个数⼩于num的时候,只会将字符串中到
    \0 的内容追加到destination指向的字符串末尾

   了解到这里我们来尝试使用一下strncat,把arr2的前3个字符追加到arr1里,如下:

#include <stdio.h>
#include <string.h>int main()
{char arr1[20] = "abcdef";char arr2[20] = "ghijklmn";strncat(arr1, arr2, 3);printf("%s\n", arr1);return 0;
}

运行结果:
在这里插入图片描述
   如果后面的第三个参数num大于arr2的大小会发生什么呢?我们来看看是否会像上面说的一样,只会把arr2拷贝完就不管了,如下图:
在这里插入图片描述
   可以看到确实跟我们描述的一致,而关于strncat的模拟实现这里也不再多讲解,可以自行实现,有问题欢迎提问

三、strncmp函数的使用

   strncmp的作用就是比较str1和str2的前num个字符,如果相等就继续往后⽐较,最多⽐较num个字⺟
   如果在这num个字符中发现不⼀样,就看此时哪个字符串更大,前者大就返回大于0的数,如果后者大,就返回小于0的数如果num个字符都相等,就是相等返回0
   我们来看看它的原型:

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

   接下来我们来使用它比较arr1和arr2前三个字符的大小,如下:

#include <stdio.h>
#include <string.h>int main()
{char arr1[20] = "abcdef";char arr2[20] = "ghijklmn";int ret = strncmp(arr1, arr2, 3);printf("在前三个字符的比较中,");if (ret > 0)printf("前一个字符串更大\n");else if (ret < 0)printf("后一个字符串更大\n");elseprintf("两个字符串相等\n");return 0;
}

运行结果:
在这里插入图片描述
   关于strncmp的实现不再赘述

四、strstr的使用和模拟实现

   这是一个对我们来说比较新的函数,我们来仔细介绍一下使用方法以及它的模拟实现,它的作用就是返回后一个字符串在前一个字符串中第⼀次出现的位置
   并且字符串的⽐较匹配不包含 \0 字符,以 \0 作为结束标志
   接下来我们来看看strstr的原型:

const char* strstr ( const char* str1, const char* str2);

   它的原型中参数看起来很简单,就是两个字符串,那它的返回值是什么呢?有两种情况,如果在字符串str1中找到了字符串str2,那么就返回str1中找到str2的起始位置,如果没有在字符串str1中找到字符串str2,那么就返回一个空指针
   所以在使用的时候,我们就可以通过它的返回值来确认是否找到对应的字符串,如果返回非空指针,那么就是找到了,返回空指针就是没有找到,接下来我们用实例来加深理解:

#include <stdio.h>
#include <string.h>void isfind(const char* x)
{if (x)printf("找到了\n");elseprintf("没找到\n");
}int main()
{char arr1[20] = "abcdef";char arr2[20] = "ghijklmn";char arr3[20] = "cde";isfind(strstr(arr1, arr2));isfind(strstr(arr1, arr3));return 0;
}

   我们来看看运行结果:
在这里插入图片描述
随后我们来对strstr进行模拟实现:

  1. 函数命名:my_strstr
  2. 函数参数:可以照抄strstr函数的原型,如下:
const char* my_strstr ( const char* str1, const char* str2);
  1. 函数实现:
    (1)第一步还是老样子,对传过来的str1和str2进行断言,确保它们不是空指针
    (2)然后我们来分析这个函数可能出现的情况,按照我们正常去想,就会想到首先就去看str1和str2指向的字符相不相等,不相等就两个指针都++,往后找,直到它们指向的字符相等
    (3)随后往后开始找,但是会出现一个问题,万一这一次匹配不上,只有前几个字符相等,后面并不相等,那我们怎么找到之前开始匹配的位置呢?如图:
    在这里插入图片描述
    (4)所以我们让str1和str2直接往后走是不妥的,这样一旦匹配失败,我们就记不住当时开始匹配的位置,也就不能进行下一次匹配,所以我们可以重新创建三个指针变量
    (5)有两个用来装下str1,一个用来代替str1往后走,一个用来记住当前第一次匹配的位置,至于str1就老老实实待在原地,不要动
    (6)还有一个用来装str2,这样如果匹配失败,就把str2重新赋值给这个变量,让它重新指向str2的开头,创建如下:
const char* my_strstr(const char* str1, const char* str2)
{assert(str1 && str2);const char* s1 = str1;//代替str1用来往后匹配字符const char* s2 = str2;//代替str2用来往后匹配字符const char* cur = str1;//记录开始匹配字符的位置
}

    (7)有了以上的经验,我们为了避免犯错,可以使用思路比较简单的暴力实现,就是创建一个循环,我们对s1进行解引用,只要里面不是结束标志\0,我们就进入循环,然后看这个位置的字符是否和str2的第一个字符相等
    (8)如果不相等,我们就让cur++,让我们的当前位置往前挪动一下,如果相等,就进行完整的匹配,这里还是有两种情况,一是如果完全匹配那么就返回cur,就是我们开始匹配时的位置,二是如果不能完全匹配那么就还是让cur++,并且将cur重新赋值给s1,将str2重新赋值给s2
    (9)现在我们就来看进行一次匹配的过程,也很简单,我们还是创建一个while循环,如果s1和s2解引用不是\0,并且解引用后相等,那我们就让s1和s2加加,一直往后走,当循环结束时,我们就判断s2解引用后是否是\0,如果是\0说明完全匹配上了,就返回cur,不是\0那么说明没有完全匹配,就让cur++,开始下一次循环查找
    (10)最后一步就是,如果我们遍历了s1中的所有字符,还是发现两个字符串没有完全匹配的地方,就直接返回空指针NULL

  1. 函数代码:
const char* my_strstr(const char* str1, const char* str2)
{assert(str1 && str2);const char* s1 = str1;const char* s2 = str2;const char* cur = str1;while (*s1){s1 = cur;s2 = str2;while (*s1 && *s2 && *s1 == *s2){s1++;s2++;}if (s2 == '\0')return cur;cur++;}return NULL;
}
  1. 函数测试:
    在这里插入图片描述
       可以看到这种情况下函数成功完成了任务,如果是更复杂的情况呢?我们继续测试:
    在这里插入图片描述
       可以看到我们实现的函数还是成功帮我们完成了任务,这里的strstr函数的实现就到此结束了,还是很有难度的,所以需要自己去手敲一遍,才能真正掌握,当然,如果有什么问题,也欢迎在评论区留言

五、strtok函数的使用

   这也是一个新函数,它的作用是什么呢?它可以按照我们给出的分隔符来分割我们的字符串,现在我们先来了解一下它的原型:

char * strtok ( char * str, const char * sep);

   它的第一个参数就是一个带有分隔符的字符串的首地址,而第二个参数就是装有分隔符的字符串的首地址,strtok函数就可以根据我们给出的分隔符将字符串分隔开来
   在分隔一次过后,它会返回这个被分隔下来的字符串的首地址,要注意一个点:在使用函数时,strtok会修改数组的内容,把分隔符改成\0,所以一般会创建源数据的副本,然后用副本使用这个函数,并且调用一次这个函数只会分隔一个分隔符,如果有多个分隔符就要多次调用
   并且第一次分隔传参时,第一个参数为要分隔的字符串的地址,而第二次及以后传参时,第一个参数只需要传一个空指针NULL,它就可以帮我们接着上一次分隔继续分隔
   如果分隔完毕,没有任何分隔符了,那么函数就会返回一个空指针来提醒我们,这就是函数strtok的基本功能
   接下来我们来总结一下它的特点:

  • sep参数指向⼀个字符串,定义了⽤作分隔符的字符集合
  • 第⼀个参数指定⼀个字符串,它包含了0个或者多个由sep字符串中⼀个或者多个分隔符分割的标记
  • strtok函数找到str中的下⼀个标记,并将其⽤ \0 结尾,返回⼀个指向这个标记的指针(注:strtok函数会改变被操作的字符串,所以被strtok函数切分的字符串⼀般都是临时拷⻉的内容并且可修改)
  • strtok函数的第⼀个参数不为 NULL ,函数将找到str中第⼀个标记,strtok函数将保存它在字符串
    中的位置
  • strtok函数的第⼀个参数为 NULL ,函数将在同⼀个字符串中被保存的位置开始,查找下⼀个标记
  • 如果字符串中不存在更多的标记,则返回 NULL 指针

   接下来我们来学习如何使用这个函数,我们可以举一个例子,比如我们的邮箱由两个分隔符分隔开来,就是@符合和(.)点号,我们想把这个邮箱地址分隔开来可以这样操作:

#include <stdio.h>
#include <string.h>int main()
{char arr1[] = "TANGLONG222@163.com";char arr2[] = "@.";char* ret1 = strtok(arr1, arr2);char* ret2 = strtok(NULL, arr2);char* ret3 = strtok(NULL, arr2);printf("%s\n%s\n%s\n", ret1, ret2,ret3);return 0;
}

   我们来看看运行结果:
在这里插入图片描述
   可以看到我们成功把这个邮箱分成了三个部分,但是有一个问题,这里有两个分隔符我们就写了三行代码,并且打印时也很麻烦,如果有n个分隔符呢?就会很麻烦,我们可以采取以下的办法:

#include <stdio.h>
#include <string.h>int main()
{char arr1[] = "TANGLONG222@163.com";char arr2[] = "@.";char* p = NULL;for (p = strtok(arr1, arr2); p != NULL; p = strtok(NULL, arr2)){printf("%s\n", p);}return 0;
}

   我们利用for循环初始化只进行一次的技巧来调用第一次的strtok,并且将它赋值给p指针,随后我们调用strtok的第一个参数就只需要传空指针,就可以写在循环的调整部分
   而中间的循环结束条件为什么设置为不等于NULL呢?因为当我们的字符串分隔完毕,没有分隔符后,函数就会返回一个空指针,p也就等于空指针了,此时用来退出循环刚好
   这个代码是不是非常巧妙呢?可能有些同学会说这种代码想不到,确实,如果没有见识过这种代码,那么确实很难想到,但是我们学习编程的过程就是模仿,模仿这些好的代码,记在心里,以后这个代码就是你的了,也就不存在想不出来的问题了

六、strerr函数的使用

   strerror 函数可以把参数部分错误码对应的错误信息的字符串地址返回来
   在不同的系统和C语⾔标准库的实现中都规定了⼀些错误码,⼀般是放在errno.h 这个头⽂件中说明的,C语⾔程序启动的时候就会使⽤⼀个全局的变量errno来记录程序的当前错误码,只不过程序启动的时候errno是0,表⽰没有错误
   但是如果当我们在使⽤标准库中的函数的时候发⽣了某种错误,就会将对应的错误码,存放在errno中,而⼀个错误码的数字是整数很难理解是什么意思,所以每⼀个错误码都是有对应的错误信息的
   strerror函数就可以将错误对应的错误信息字符串的地址返回,可以用%s的形式将错误信息打印出来
   我们可以试着打印前10个错误码对应的错误信息,如下:

#include <stdio.h>
#include <string.h>int main()
{int i = 0;for (i = 0; i < 10; i++){printf("%s\n", strerror(i));}return 0;
}

在Windows11+VS2022环境下输出的结果如下:
在这里插入图片描述
我们可以测试使用文件操作测试一下:

#include <stdio.h>
#include <string.h>
#include <errno.h>
int main ()
{FILE * pFile;pFile = fopen ("data.txt","r");if (pFile == NULL)printf ("fopen: %s\n", strerror(errno));return 0;
}

   在代码目录上并没有data.txt这个文件,自然不能打开文件进行读操作,所以这个代码会在errno.h中产生一个错误码,这个时候我们就把这个错误码打印出来,我们来看看代码运行结果:
在这里插入图片描述
   结果显示没有这样的文件或者文件夹,正好跟我们设计的错误一致
接下来也可以了解⼀下 perror 函数,perror函数的使用更加简单方便,可以直接将错误信息打印出来
   perror函数的参数里可以写上可能出现错误的命令的名字,它可以打印参数部分的字符串后,再打印⼀个冒号和⼀个空格,再打印错误信息
   比如:

#include <stdio.h>
#include <string.h>
#include <errno.h>
int main ()
{FILE * pFile;pFile = fopen ("data.txt","r");if (pFile == NULL)perror("fopen");return 0;
}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/436348.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Linux:线程概念】

目录 概念&#xff1a; 创建线程的函数&#xff1a;​编辑 ​编辑 有多进程为什么还需要有多线程&#xff1f; 线程调度的成本为什么低&#xff1f; 进程与线程的区别&#xff1a; 概念&#xff1a; 线程是CPU的基本调度单位&#xff0c;在进程内部运行。在内核中&#xff…

数据库系统

数据库管理系统 DBMS Database Management System分为三类&#xff1a; 关系数据库系统&#xff08;Relation Database System&#xff09; 面向对象数据库系统 (Object-Oriented Database System) 对象关系数据库系统 (Object-Oriented Relation Database System) 数据库设…

Stable Diffusion绘画 | SDXL模型使用注意事项

注意事项 SDXL模型的使用&#xff0c;对电脑配置要求更高&#xff0c;需要 8GB 以上显存的显卡SDXL模型兼容性不太好&#xff0c;容易出现错误&#xff0c;对 Mac 电脑不友好只能选择 SDXL模型 训练的 LoRA 使用不能使用旧的 VAE文件 SDXL 专用 VAE 文件&#xff1a;sdxl_vae.…

在矩池云使用 Llama-3.2-11B-Vision 详细指南

Llama 3.2-Vision是Meta开发的一系列多模态大型语言模型&#xff08;LLMs&#xff09;&#xff0c;包含11B和90B两种规模的预训练和指令调整模型。 这些模型专门优化用于视觉识别、图像推理、字幕生成和回答有关图像的一般问题。Llama 3.2-Vision模型在常见行业基准测试中的表…

【网络安全】内部应用中的多重漏洞利用

未经许可,不得转载。 文章目录 初步发现:帐户枚举利用帐户枚举发现 IDOR 导致帐户接管拦截请求洪水攻击:注册拒绝服务目标网站:https://redacted.com 初步发现:帐户枚举 在最近的一次渗透测试中,我对一个仅供员工使用的内部应用程序进行了评估,重点关注身份验证和帐户…

HR告诉你:HCIE证书到底是职场神话还是锦上添花?真相大解析

在职场内卷的赛道上&#xff0c;每个人都在寻找能让自己脱颖而出的光环。而HCIE证书&#xff0c;作为IT领域的一项高含金量认证&#xff0c;莫过于优势最高最让人垂涎的光环&#xff0c;许多人相信它能开启通往理想职位的大门。 但在这个快速变化的时代&#xff0c;HCIE证书真的…

基于Hive和Hadoop的电商消费分析系统

本项目是一个基于大数据技术的电商消费分析系统&#xff0c;旨在为用户提供全面的电商消费信息和深入的消费行为分析。系统采用 Hadoop 平台进行大规模数据存储和处理&#xff0c;利用 MapReduce 进行数据分析和处理&#xff0c;通过 Sqoop 实现数据的导入导出&#xff0c;以 S…

望繁信科技CTO李进峰受邀在上海外国语大学开展流程挖掘专题讲座

2023年&#xff0c;望繁信科技联合创始人兼CTO李进峰博士受邀在上海外国语大学国际工商管理学院&#xff08;以下简称“上外管院”&#xff09;开展专题讲座&#xff0c;畅谈流程挖掘的发展及对企业数字化转型的价值。演讲吸引了上外教授和来自各行各业的领军企业学员百余人。 …

easyExcel使用模版填充excel,合并单元格

一、最终效果 二、制作模版 1、制作填充模版 模版在代码中保存的位置 2、Controller /*** 下载模板*/ RequestMapping(value "exportData") public void exportData(KqKqb kqKqb,HttpServletResponse response, HttpServletRequest request) throws IOExceptio…

高级算法设计与分析 学习笔记10 平摊分析

动态表&#xff0c;可以变长。 一溢出就另起一个两倍大小的表。 可以轻易证明把n个数字放进去的时间复杂度是O(n)&#xff0c;n n/2 n/4……也就2n&#xff0c;插入数字本身也就是n&#xff0c;加起来最多不超过3n. 这种复杂度究竟是怎么算的&#xff1f;毕竟每次插入复杂度…

Vulhub zico 2靶机详解

项目地址 https://download.vulnhub.com/zico/zico2.ova实验过程 将下载好的靶机导入到VMware中&#xff0c;设置网络模式为NAT模式&#xff0c;然后开启靶机虚拟机 使用nmap进行主机发现&#xff0c;获取靶机IP地址 nmap 192.168.47.1-254根据对比可知Zico 2的一个ip地址为…

阿里云ACP认证考试题库

最近有好些同学&#xff0c;考完阿里云ACP了&#xff0c;再来跟我反馈&#xff1a;自己花700买的阿里云ACP题库&#xff0c;结果答案是错的&#xff01; 或者考完后发现&#xff0c;买的阿里云ACP题库覆盖率只有50%&#xff01; 为避免大家继续踩坑&#xff0c;给大家分享一个阿…

短视频去水印解析api接口使用文档

短视频去水印解析api接口&#xff0c;支持各大平台短视频和图集。 请求示例&#xff1a;https://www.dspqsy.vip/spapi?key密钥&url短视频链接 返回数据格式&#xff1a;JSON 请求方式&#xff1a;GET/POST 请求参数&#xff1a;url (短视频分享的URL) PHP 源码&…

从存储到人工智能洞察: 利用 MinIO 和 Polars 简化数据管道

将 MinIO 的高性能、可扩展企业对象存储的强大功能与 Polars&#xff08;闪电般快速的 DataFrame 库&#xff09;的快速内存数据处理功能相结合&#xff0c;可以显著提高数据管道的性能。在 AI 工作流中尤其如此&#xff0c;其中预处理大型数据集和执行特征选择是关键步骤。在这…

Linux操作系统中dubbo

1、简介 dubbo框架是做微服务通信的&#xff0c;是由阿里巴巴开发&#xff0c;后捐赠给阿帕奇基金会。 2、与OpenFeign的区别 dubbo是采用RPC协议实现微服务通信&#xff0c;OpenFeign是采用Http请求的方式实现的。 OpenFeign 最简单的&#xff0c;就是Spring公司开发的&am…

RabbitMQ 队列之战:Classic 和 Quorum 的性能洞察

RabbitMQ 是一个功能强大且广泛使用的消息代理&#xff0c;它通过处理消息的传输、存储和交付来促进分布式应用程序之间的通信。作为消息代理&#xff0c;RabbitMQ 充当生产者&#xff08;发送消息的应用程序&#xff09;和使用者&#xff08;接收消息的应用程序&#xff09;之…

2024年软考网络工程师中级题库

1【考生回忆版】以下不属于5G网络优点的是&#xff08;A) A.传输过程中消耗的资源少&#xff0c;对设备的电池更友好 B.支持大规模物联网&#xff0c;能够连接大量低功耗设备&#xff0c;提供更高效的管理 C.引入了网络切片技术&#xff0c;允许将物理网络划分为多个虚拟网络…

Elasticsearch7.7.1集群不能相互发现的问题解决以及Elasticsearch7.7.1安装analysis-ik中文分词插件的应用

一、Elasticsearch7.7.1集群不能相互发现的问题解决 在使用elasticsearch7.7.1搭建集群&#xff0c;使用了3台服务器作为节点&#xff0c;但在搭建的过程中发现每台服务器的elasticsearch服务都正常&#xff0c;但是不能相互发现&#xff0c;期间进行了一些配置的修改偶尔出现了…

uniapp中实现评分组件,多用于购买商品后,对商品进行评价等场景

前言 uni-rate是uniapp框架中提供的一个评分组件。它可以用于用户评价、打分等场景。uni-rate组件可以根据设定的星星总数&#xff0c;展示用户评分的效果&#xff0c;用户可以通过点击星星或滑动星星的方式进行评分。同时&#xff0c;uni-rate组件也支持自定义星星图标、星星…

Vue 技术进阶 day2 数据监视的原理、其他内置指令、自定义指令、生命周期、组件化、VueComponent构造函数

目录 1.Vue监测数据的原理 1.1 原理 1.1.1 数据劫持 1.1.2 观察者模式(Vue内部的实现) 1.1.3 更新组件 1.1.4 计算属性和侦听器 1.2 后添加属性做响应式&#xff08;Vue.set / vm.$set&#xff09; 1.3 对象和数组的响应式 1.4 数据监视案例 2.指令 2.1 内置指令 2.…