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

news2024/12/23 10:20:41

在这里插入图片描述

文章目录

  • 一、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");
	else
		printf("两个字符串相等\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");
	else
		printf("没找到\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.coloradmin.cn/o/2182185.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

智能招聘系统小程序的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;用户管理&#xff0c;企业管理&#xff0c;招聘信息管理&#xff0c;应聘信息管理&#xff0c;系统管理 微信端账号功能包括&#xff1a;系统首页&#xff0c;招聘信息&#xff0c;我的 开发系统&#…

企望制造ERP系统存在RCE漏洞

漏洞描述 企望制造纸箱业erp系统由深知纸箱行业特点和业务流程的多位IT专家打造&#xff0c;具有国际先进的管理方式&#xff0c;将现代化的管理方式融入erp软件中&#xff0c;让企业分分钟就拥有科学的管理经验。erp的功能包括成本核算、报价定价、订单下达、生产下单、现场管…

鸿蒙NEXT开发环境搭建(基于最新api12稳定版)

注意&#xff1a;博主有个鸿蒙专栏&#xff0c;里面从上到下有关于鸿蒙next的教学文档&#xff0c;大家感兴趣可以学习下 如果大家觉得博主文章写的好的话&#xff0c;可以点下关注&#xff0c;博主会一直更新鸿蒙next相关知识 专栏地址: https://blog.csdn.net/qq_56760790/…

【设计模式-命令】

定义 命令模式&#xff08;Command Pattern&#xff09;是一种行为设计模式&#xff0c;它将请求封装为一个对象&#xff0c;从而使您能够使用不同的请求、排队请求或记录请求&#xff0c;并支持可撤销的操作。该模式通过将请求与其执行分离&#xff0c;使得请求者和接收者之间…

养生之道,首先在于饮食!

在快节奏的现代生活中&#xff0c;养生健康成为了人们日益关注的话题。良好的生活习惯和科学的养生方式&#xff0c;不仅能够提升我们的生活质量&#xff0c;还能有效预防疾病&#xff0c;让我们拥有更加充沛的精力和更长久的生命力。 养生之道&#xff0c;首先在于饮食。均衡…

cpp,git,unity学习

c#中的? 1. 空值类型&#xff08;Nullable Types&#xff09; ? 可以用于值类型&#xff08;例如 int、bool 等&#xff09;&#xff0c;使它们可以接受 null。通常&#xff0c;值类型不能为 null&#xff0c;但是通过 ? 可以表示它们是可空的。 int? number null; // …

如何使用 Gradio 创建聊天机器人

如何使用 Gradio 创建聊天机器人 文章目录 如何使用 Gradio 创建聊天机器人一、介绍二、简单示例与实战1、定义聊天功能2、示例&#xff1a;回答“是”或“否”的聊天机器人3、另一个使用用户输入和历史记录的示例4、流式聊天机器人 三、定制化聊天机器人1、为您的机器人添加更…

docker-compose 快速部署clickhouse集群

在本教程中&#xff0c;我们将学习如何使用 Docker Compose 部署一个带有三节点的 ClickHouse 集群&#xff0c;并使用 ZooKeeper 作为分布式协调服务。 前提条件 注意事项&#xff1a; 镜像版本号注意保持一致 [zookeeper:3.7, clickhouse/clickhouse-server:22.5.4]config…

清华大学、腾讯联合推全开源多模态架构Oryx 支持超长视频输入

在人工智能快速发展的今天&#xff0c;一个名为ORYX的多模态大型语言模型正在悄然改变我们对AI理解视觉世界能力的认知。这个由清华大学、腾讯和南洋理工大学研究人员联合开发的AI系统&#xff0c;堪称视觉处理领域的"变形金刚"。 ORYX&#xff0c;全称Oryx Multi-M…

Kotlin:2.0.20 的新特性

一、概述 Kotlin 2.0.20英文版官方文档 Kotlin 2.0.20发布了!这个版本包括对Kotlin 2.0.0的性能改进和bug修复&#xff0c;我们在其中宣布Kotlin K2编译器为Stable。以下是本次发布的一些亮点: 数据类复制函数将具有与构造函数相同的可见性来自默认目标层次结构的源集的静态访…

Windows下载安装Minio超详细

1.下载地址 服务端文件:minio.exe 用于接收文件信息。 客户端文件:mac.exe 用于上传文件 &#xff0c;如果用程序代码操作文件存储&#xff0c;只启动服务端即可。 #企业版 https://min.io/download?licenseenterprise&platformkubernetes#/windows #社区版&#xff08;…

TiDB 性能测试的几个优化点

作者&#xff1a; 数据源的TiDB学习之路 原文来源&#xff1a; https://tidb.net/blog/513a4eef 背景 前段时间参与了一个 TiDB 的性能测试&#xff0c;具体是在三台海光服务器&#xff08;512G内存、128 core 分8个NUMA、4块3.5T SSD&#xff09;搭建一个混合部署的 TiDB …

嵌入式中C语言小项目的具体实现

大家好&#xff0c;今天主要给大家分享一下&#xff0c;如何使用C语言来实现对应的小项目。 第一&#xff1a;C语言计算器实现 第二&#xff1a;C项目中猜字游戏实现 第三&#xff1a;C语言简单的日历实现 第四&#xff1a;C语言中每日定投债券基金一年能赚多少

浮动与网格系统

控制页面布局的工具有浮动、Flexbox 和定位等&#xff0c;这些工具本身没有优劣支付&#xff0c;只不过实现布局的方式略有不同。 1 浮动 浮动元素会脱离正常的文档流&#xff0c;并向左或向右移动&#xff0c;直到它的边缘碰到包含框或另一个浮动元素的边框为止。 文本和内联…

损失函数篇 | YOLOv5 引入Unified-IoU 高质量目标检测IoU损失

论文地址:https://arxiv.org/pdf/2408.06636 开源代码地址:https://github.com/lxj-drifter/UIOU_files 目标检测是计算机视觉领域的重要组成部分,其效果直接由预测框的回归精度决定。作为模型训练的关键,IoU(交并比)很好地展示了当前预测框与真实框(Ground Truth)之间…

数据结构——队列的基本操作

前言 介绍 &#x1f343;数据结构专区&#xff1a;数据结构 参考 该部分知识参考于《数据结构&#xff08;C语言版 第2版&#xff09;》24~28页 &#x1f308;每一个清晨&#xff0c;都是世界对你说的最温柔的早安&#xff1a;ૢ(≧▽≦)و✨ 目录 前言 1、队列的基本概念…

Flutter 3.24 AAPT: error: resource android:attr/lStar not found.

在Android build,gradle下面&#xff0c;添加右边红框的代码&#xff1a; subprojects {afterEvaluate { project ->if (project.plugins.hasPlugin("com.android.application") ||project.plugins.hasPlugin("com.android.library")) {project.androi…

复写零——双指针算法

题目链接 复写零https://leetcode.cn/problems/duplicate-zeros/description/ 题目要求 样例 题目分析 先看示例1&#xff0c;题目要求将数组中所有的0&#xff0c;均复写一遍&#xff0c;且要在原数组上进行更改&#xff0c;多余的元素消失 但我们发现&#xff0c;如果双指针…

04DSP学习-利用syscfg配置EPWM

打开syscfg文件&#xff0c;左侧control栏中找到EPWM&#xff0c;点击&#xff0c;发现TI提供了一些帮助文档&#xff0c;帮助了解如何使用syscfg以及如何了解EPWM。我们结合配置过程去理解如何使用。 设计目标 使用EPWM1&#xff1b;增减计数&#xff1b;PWM频率为10kHz&…

ios内购支付-支付宝APP支付提现

文章目录 前言一、IOS内购支付&#xff08;ios订单生成自己写逻辑即可&#xff09;1.支付回调票据校验controller1.支付回调票据校验server 二、安卓APP支付宝支付1.生成订单返回支付宝字符串&#xff08;用于app拉起支付宝&#xff0c;这里用的是证书模式&#xff09;2.生成订…