C语言函数: 字符串函数及模拟实现strtok()、strstr()、strerror()

news2024/11/27 17:54:35

C语言函数: 字符串函数及模拟实现strtok()、strstr()、strerror()

strstr()函数:

        作用:字符串查找。在一串字符串中,查找另一串字符串是否存在。

形参:

4b2e6b20082d41528eadfab74266debc.png

       str2在str1中寻找。返回值是char*的指针

        原理:如果在str1中找到了str2,则返回在str1中存在的str2的字符串的第一个字符的地址。如果在str1中每找到str2,则返回NULL(空指针)。

举例:

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>

int main()
{
	char arr1[] = "abcdefgh";
	char arr2[] = "bcde";
	char arr3[] = "bcdeh";
	char* a1 = strstr(arr1, arr2);
	char* a2 = strstr(arr1, arr3);
	return 0;
}

        a1是在arr1中寻找arr2,在arr1[1]~arr1[4]上找到了与arr2一样的字符串,则返回arr1中字符'b'的地址。

        a2是在arr1中寻找arr3,在arr1中并未找到与arr1一样的字符串,则返回NULL。

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>

int main()
{
	char arr1[] = "abcdefgh";
	char arr2[] = "bcde";
	char arr3[] = "bcdeh";
	char* a1 = strstr(arr1, arr2);
	char* a2 = strstr(arr1, arr3);
	if (a1 != NULL)
	{
		printf("arr2找到了");//结果:arr2找到了
	}
	if (a2 == NULL)
	{
		printf("arr3没找到");//结果:arr3没找到
	}

	return 0;
}

strstr()函数的模拟实现:

char* my_strstr(const char* str1, const char* str2)//因为两个地址值不会改变,所以加const
{
	assert(str1 && str2);//防止两个参数为NULL
	char* pt1 = NULL;
	char* pt2 = NULL;
	char* nc = str1;//用于在字符串中找到与str2首元素一样字符。
	while (*nc)//当str1中找完了,都没有找到和str2中首元素一样的字符时停止循环。
	{
		pt1 = nc;//用于定位到每次找到和str2中首元素一样的字符的地址。
		pt2 = str2;//用于指向str2的首元素地址。
		while (*pt1 && *pt2 && ( * pt1 == *pt2))//找到和str2中首元素一样的字符
		{
			pt1++;
			pt2++;
		}
		if (*pt2 == '\0')//当上面的循环走完,ptr2的指向\0时,说明在str1中找到了和str2一样的字符串
		{
			return nc;//返回当时的字符串。
		}
		nc++;//当这个str1中的元素和str2不匹配,就要看下一个字符是否匹配。
	}
	return NULL;
}

        

int main()
{
	char arr1[] = "abbcdef";
	char arr2[] = "bcd";
	char* a1 = my_strstr(arr1, arr2);
	if (a1 != NULL)
	{
		printf("arr2找到了\n");//结果:arr2找到了
	}
	else
	{
		printf("arr2没找到\n");
	}
	return 0;
}

        

补充:还有一种算法:KMP算法。也是一个字符串查找的算法。


strtok()函数:

        作用:对字符串进行切分(分割)

        #include <string.h>

参数:

a8252c78062a410c91484cd68d05255b.png

        str是将被分割的字符串

        sep是需要传入的是字符串,sep内包含了需要作为分割符的字符的集合

        71ff5a9b43c14eddb52b3d06b567d768.png

        如果要将str中分为三个部分:www、qq、com

        那么就需要在@和.处进行分割。因此,@和.就是分割符,将分隔符放入sep字符串中,此时就是 :分割符的字符的集合

       

原理:

       这里有个字符串str :"www@qq.com\0"

        strtok()会将按照sep所指示的,在str中寻找sep内存在的字符。

        找到@后,会将@改成\0。

        strtok()返回@之前的字符串的首元素地址,即:str中第一个'w'的地址。

        并且strtok会记录此时\0的位置。

        strtok有两种机制:

        1.如果strtok不为NULL,那么会保存@的位置(@会被改成\0)     

        2.如果strtok为 NULL,那么会从上次strtok所保存@的位置,继续往下找seq中的字符,如果找到,接着分割。最后再保存被改成\0的位置。

        最后:如果strtok找到了没被改的\0,也就是字符串末尾的\0时,返回NULL。

        

        那么有疑问了,strtok是怎么保存@的位置呢?

        其实,想要保存某个数据,实际上就是创建一个全局变量。只要创建了全局变量,程序运行到哪里,无论在哪个函数中,都可以被使用。

        因此,strtok就是利用全局变量的能力,从而保存了@的位置。那么strtok是怎么保存的呢?难道直接在代码中写上一个全局变量吗?显然不是。

        strtok其实是使用static修饰了存放@位置的变量,因为static基本含义就是让一个变量成为全局变量。

        static函数名除了对该函数声明的文件可见外,其他文件都无法访问。也就是只能被本文件中的函数调用,而不能被同一程序中的其他文件的函数调用。

        static可以限定变量或函数为静态存储。静态函数会被自动分配在一个一直使用的存储区,直到程序结束才从内存消失,static限定的变量或函数不会和同一程序中其他文件同名的相冲突。如果用static限定内部变量,则该变量从程序一开始就用有内存,不会随其所在函数的调用和退出而分配和消失。

        

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include <assert.h>

void cc()
{
	static int opp = 0;
	opp++;
	printf("%d", opp);
}

int main()
{
	cc();//结果:1
    int opp = 3;
	cc();//结果:2
	return 0;
}

使用建议:

        因为strtok的会对str字符串本身进行修改,如果不想对strtok进行修改时,可以用strcpy进行拷贝,然后对拷贝的字符串使用strtok

        

char arr[] = "www@qq.com";
	char* p = "@.";
	char tmp[20] = { 0 };
	strcpy(tmp, arr);//拷贝

实验:

        

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include <assert.h>
#include <string.h>

int main()
{
	char arr[] = "www@qq.com";
	char* p = "@.";
	char tmp[20] = { 0 };
	strcpy(tmp, arr);
	char* ret = NULL;
	ret = strtok(tmp, p);
	printf("%s\n", ret);//www
	ret = strtok(NULL,p);
	printf("%s\n", ret);//qq
	ret = strtok(NULL, p);
	printf("%s\n", ret);//com
	ret = strtok(NULL, p);
	printf("%s\n", ret);//(null)
	return 0;
}

        创建了一个ret用于存放,每次strtok返回的字符串首元素地址。

        第一次调用 strtok(tmp, p)拿到了(返回)'w'的地址,保存了@(\0)的位置。

        第二次调用strtok(NULL,p)拿到了(返回)'q'的地址,保存了.(\0)的位置。

        第三次调用strtok(NULL,p)拿到了(返回)'c'的地址,\0的位置。

        第四次调用strtok(NULL,p)拿到了(返回)NULL,这是因为\0后面找不到p内包含的字符了。

        结果打印三个部分:www、qq、com

代码强化:

        这一个一个地调用strtok显然是不合理地。

        并且我们发现除了第一次调用strtok,传入地第一个参数是一个活指针之外,后面的几次调用使用的都是NULL,因此我们可以创建一个循环,从而分割它们。

        

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include <assert.h>
#include <string.h>

int main()
{
	char arr[] = "www@qq.com";
	char* p = "@.";
	char tmp[20] = { 0 };
	strcpy(tmp, arr);
	char* ret = NULL;
	for ( ret = strtok(tmp, p); ret != NULL; ret = strtok(NULL, p))
	{
		printf("%s\n", ret);
	}
    return 0;
}

        for(初始化值;判断条件;对值改变)

        初始化值,执行了strtok第一次调用。

        判断条件,如果为strtok返回了NULL,说明没有分隔符了,for循环就停止。

        对值改变,做到了除第一次以外,后n次地对strtok地调用。

        

模拟实现:

       C语言源码剖析与实现——strtok()系列函数实现_strtok源码_C+G的博客-CSDN博客


strerror

        作用:翻译错误码。在使用库函数的时候,如果失败了都会出现错误码,而这串错误码你完全不懂是什么意思,比如:5,这个错误码的意思是什么呢?肯定不知道吧。

        错误码的作用是告诉你,你的代码执行过程中出现的错误。比如内存空间不足等等...

参数:

        头文件:#include <string.h>

d954914288674d1ebd423beeb356dcfd.png

        strerror返回的是一个地址,这个地址指向了一个字符串。这个字符串就是strerror从错误码所翻译出来的一句话。

        

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <errno.h>

int main()
{
	printf("%s\n", strerror(0));
	printf("%s\n", strerror(1));
	printf("%s\n", strerror(2));
	return 0;
}

 

        除此之外,还有许多C语言内部的错误码。

errno:

        errno是C语言定义的一个全局变量,它的作用是存放代码执行过程中出现的错误码。

        它不需要人为的定义,当用到它的时候只需要引头文件:#include <errno.h>

        

举例:

        

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <errno.h>

int main()
{
	//fopen:打开文件
	FILE* pf = fopen("test.txt", "r");//test.txt不存在
	if(pf ==NULL)//fopen找不到test.txt会返回空指针
		printf("%s\n", strerror(errno));//返回错误信息
	return 0;
}

模拟实现:

        单纯的翻译代码,因此不做实现。

 

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

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

相关文章

为什么这几年参加PMP考试的人越来越多?

PMP认证从国外引进大陆这么多年了&#xff0c;其火热程度依然不减&#xff0c;我个人认为是取决于市场的运作和“游戏规则”&#xff08;岗位招聘和项目招标需要&#xff09;。PMP含金量不算高&#xff0c;更多的是“敲门砖”作用&#xff0c;项目管理岗位的门槛&#xff0c;当…

MySQL日志审计和分析

审核数据库活动是加强数据库安全性的重要组成部分。这涉及识别漏洞&#xff0c;例如默认或弱登录凭据、过多的用户和组权限以及未修补的数据库。攻击者利用这些漏洞来实现自己的目标&#xff0c;如权限提升、SQL 注入和 DoS 攻击。因此&#xff0c;出于安全性和合规性原因&…

源码分析spring如和对@Component注解进行BeanDefinition注册的

Spring ioc主要职责为依赖进行处理&#xff08;依赖注入、依赖查找&#xff09;、容器以及托管的(java bean、资源配置、事件)资源声明周期管理&#xff1b;在ioc容器启动对元信息进行读取&#xff08;比如xml bean注解等&#xff09;、事件管理、国际化等处理&#xff1b;首先…

SPI读写SD卡速度有多快?

SD卡是一个嵌入式中非常常用的外设&#xff0c;可以用于存储一些大容量的数据。但用单片机读写SD卡速度一般都有限&#xff08;对于高速SD卡&#xff0c;主要是受限于单片机本身的接口速度&#xff09;&#xff0c;在高速、实时数据存储时可能会有影响。但具体速度可以达到多少…

Spark RDD的设计与运行原理

一、Spark RDD概念 一个RDD就是一个分布式对象集合&#xff0c;本质上是一个只读的分区记录集合&#xff0c;每个RDD可以分成多个分区&#xff0c;每个分区就是一个数据集片段&#xff0c;并且一个RDD的不同分区可以被保存到集群中不同的节点上&#xff0c;从而可以在集群中的…

Could not resolve dependencies for project

maven 打包Could not resolve dependencies for project和无效的目标发行版: 1.8 1.maven 打包Could not resolve dependencies for project 最近项目上使用的是idea ide的多模块话&#xff0c;需要模块之间的依赖&#xff0c;比如说系统管理模块依赖授权模块进行认证和授权&a…

聊聊关于分类和分割的损失函数:nn.CrossEntropyLoss()

目录 1. nn.CrossEntropyLoss() 2. 多分类中 nn.CrossEntropyLoss() 的应用 3. 分割中 nn.CrossEntropyLoss() 的应用 3.1 测试文件 3.2 输出可视化 3.3 softmax 3.4 log 3.5 CrossEntropyLoss 1. nn.CrossEntropyLoss() 分类中&#xff0c;经常用 nn.CrossEntropyL…

.NET Core Api使用Folder(文件夹)形式发布并指定监听端口

分为以下几个步骤 1. 先安装SDK及运行环境, 无需安装IIS, 因为他不在IIS上运行 环境下载路径, 我用的是.NET 7.0, 可以根据自己的版本下载: 下载 .NET 7.0 SDK (v7.0.201) - Windows x64 Installer 下载.NET运行环境 下载后安装.直接下一步..安装即可 2. 配置发布设置 (…

Nginx服务优化与防盗链

目录 1.隐藏nginx版本号 1.查看版本号 2.隐藏版本信息 2.修改用户与组 3.缓存时间 4.日志分割 5.连接超时 6.更改进程数 7.网页压缩 8.配置防盗链 1.配置web源主机&#xff08;192.168.156.10 www.lhf.com&#xff09; 2.配置域名映射关系 3.配置盗链主机 &#xff0…

python实现波士顿房价预测---(2)

计算梯度 继续上一篇的内容python实现波士顿房价预测—(1)。 梯度计算公式中引入计算因子12\frac{1}{2}21​&#xff0c;为了计算更加简洁。 L12N∑i1N(yi−zi)2L\frac{1}{2N}\sum_{i1}^{N}(y_i - z_i)^2L2N1​∑i1N​(yi​−zi​)2 其中ziz_izi​是模型对于第i个样本的预测值…

【halcon】轮廓拟合相关函数

涉及函数 edges_sub_pix 寻找边缘 edges_sub_pix (Image, Edges, canny, 1, 10, 20) 后面三个参数&#xff0c;越小&#xff0c;找到的细节越多。这个是对应录波器为canny时。 canny滤波器用的最多。 segment_contours_xld 将连续的轮廓进行分段&#xff0c;按圆弧或者执…

软件测试13

Linux命令 1.pwd&#xff1a;查看当前所在的路径位置 2.ls&#xff1a;查看当前路径下有哪些文件 3.cd&#xff1a;切换路径 4.touch&#xff1a;创建普通文件&#xff0c;可以创建单文件&#xff0c;也可以创建多文件&#xff08;touch a&#xff0c;touch b c&#xff09; 5…

【专项训练】高级搜索

高级搜索,这部分非常烧脑,可略过! 包括:剪枝、双向BFS、启发式搜索! 启发式搜索:优先队列,即优先级搜索! 回溯:分治 + 试错 数独问题,类似八皇后! 36. 有效的数独 https://leetcode.cn/problems/valid-sudoku/description/ class Solution(object

Java【二叉搜索树和哈希表】模拟实现 + 【Map和Set】介绍

文章目录前言一、二叉搜索树1、什么是二叉搜索树2、模拟实现二叉搜索树2.1, 查找2.2, 插入2.3, 删除3、性能分析二、模型三、哈希表1、什么是哈希表1.1, 什么是哈希冲突1.2, 避免, 解决哈希冲突1.2.1, 避免: 调节负载因子1.2.2, 解决1: 闭散列(了解)1.2.3, 解决2: 开散列/哈希桶…

基于卷积神经网络CNN的水果分类预测,卷积神经网络水果等级识别

目录 背影 卷积神经网络CNN的原理 卷积神经网络CNN的定义 卷积神经网络CNN的神经元 卷积神经网络CNN的激活函数 卷积神经网络CNN的传递函数 卷积神经网络CNN水果分类预测 基本结构 主要参数 MATALB代码 结果图 展望 背影 现在生活&#xff0c;为节能减排&#xff0c;减少电能…

推荐系统 FM因式分解

reference&#xff1a;知乎 FM算法解析 LR算法没有二阶交叉 如果是id类特征&#xff0c;这里的x是0/1&#xff0c;raw的特征输入就是float&#xff0c;当然&#xff0c;在我的理解里&#xff0c;一般会把raw的特征进行分桶&#xff0c;还是映射到0/1特征&#xff0c;不然这个w…

树莓派测试wifi与eth速率

测试网速方法&#xff1a; 1.安装插件&#xff1a; 首先在树莓派端安装iperf3 sudo apt install iperf3PC端也需要安装iperf3&#xff0c;单击下面网址即可 下载网址 压缩包解压到桌面&#xff0c;文件内容如下图所示&#xff1a; 2.开始测速服务&#xff1a; 树莓派端在…

周报终结者 GitLab 个人工作记录查询器

序言 每周都要写周报&#xff0c;烦死人。为了解救自己&#xff0c;把自己从无聊的工作中抽离出来。 特别写了一个工具。可以查询GitLab中自己一段时间内的所有提交记录。 按照项目和分支进行排序 效果 还可以查询原始的json数据方便自己进行筛选和扩展 使用方式 1.获取个人…

从 1 秒到 10 毫秒!在 APISIX 中减少 Prometheus 请求阻塞

本文介绍了 Prometheus 插件造成长尾请求现象的原因&#xff0c;以及如何解决这个问题。 作者屠正松&#xff0c;Apache APISIX PMC Member。 原文链接 现象 在 APISIX 社区中&#xff0c;曾有部分用户陆续反馈一种神秘现象&#xff1a;部分请求延迟较长。具体表现为&#xf…

Android电视盒子最强看电视app-tvbox配置(视频源)教程

今天给大家分享一下安卓tv上最强的看视频神器-tvbox的配置方法 tvbox是一款影视观看类的软件&#xff0c;各种影视资源都是为你免费提供的&#xff0c;还有海量热门影视为你提供电视直播&#xff0c;让你可以实时在线进行观看以及体验一样&#xff0c;超多影视剧内容你感兴趣的…