C字符串和内存函数介绍(三)——其他的字符串函数

news2024/9/21 22:53:03

在#include<string.h>的这个头文件里面,除了前面给大家介绍的两大类——长度固定的字符串函数长度不固定的字符串函数。还有一些函数以其独特的用途占据一席之地。

今天要给大家介绍的是下面这三个字符串函数:strstr,strtok,strerror

学习指南:对于strstr函数有能力的小伙伴可以掌握一下它的模拟实现,但是strtok函数和strerror函数只需要知道它们的底层原理和基本使用即可。

———————————————————————————————————————————

附(难度评析表):

学习难度:

strstr:  ⭐⭐⭐⭐⭐

strtok:     ⭐⭐⭐

strerror:   ⭐

理解难度:

strstr:    ⭐ ⭐ ⭐(不考虑Kmp的部分,加上Kmp它完全可以达到五颗星的理解难度)

strtok: ⭐ ⭐ ⭐ ⭐

strerror: ⭐

目录

一、strstr函数:

1. 介绍:

2. 模拟实现:

二、strtok函数:

1. 介绍:

2. strtok函数的应用场景与工作原理:

应用场景:

工作原理:

(第一种情况)——str参数不为NULL指针:

(第二种情况)——str参数为NULL指针:

三、strerror函数:

1. 介绍:

2. 关于错误码:

3. perror函数的使用:


一、strstr函数:

1. 介绍:

函数原型:

const char * strstr ( const char * str1, const char * str2 );
函数名功能描述头文件
strstr在str1字符串查找str2字符串#include<string.h>

返回值说明:返回一个指针,该指针指向了str2在str1中首次出现的位置。如果str2不在str1里面,则返回一个NULL指针。

2. 模拟实现:

思路解析:

养成编程的好习惯:我们让str1和str2始终指向各自的字符串的首元素地址不动它们,这样一来可以避免在之后的查找遍历过程中,因为找不到str1和str2首元素的地址而无法回溯的问题。然后分别定义两个指针s1,s2遍历str1数组和str2数组。

如果对应位置两个字符串的值相等(即:s1 == s2),我们就让s1和s2同时往下走(s1++,s2++)。反之让s1回到最开始查找的位置的下一个位置(该位置我们可以用一个cp指针进行标记)。然后s2回到str2的位置(即子串的第一个元素的位置)。

然后重复上面的步骤,大概思路就是这样的。另外的话考虑到如果str1字符串剩余待检索字符不足str2的实际长度,那也就没必要继续检索了,所以在这之前我们可以先求一下str2数组的长度(这个过程可以用strlen函数,也可以自己写代码)。详情请见代码:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<assert.h>
//模拟实现strstr函数的功能:(暴力求解)

const char* my_strstr(const char* str1, const char* str2)
{
	const char* cp = str1;//记录开始遍历时的位置
	const char* s1 = str1;//遍历str1的指针
	const char* s2 = str2;//遍历str2的指针
	int cnt = 0;
	while (*s2++)
	{
		cnt++;
	}
	while (*(cp+cnt-1))
	{
		s1 = cp;
		s2 = str2;
		while (*s2 && *s1 && *s1 == *s2)
		{
			s1++;
			s2++;
		}
		if (*s2 != '\0')
		{
			cp++;
		}
		else
		{
			return cp;
		}
	}
	return NULL;
}

int main()
{
	const char* str1 = "abbbcef";
	const char* str2 = "bc";
	const char* ret = my_strstr(str1, str2);
	if (ret)
	{
		printf("%s", ret);
	}
	else
	{
		printf("str1里面找不着str2");
	}
	return 0;
}

上面的代码实际上一种暴力求解的思路,当然也有地方把这也叫作BF算法,这个是解决这种问题的通法。但是不是最高效的算法,这个在一个字符里面找另一个字符普遍认为最高效是KMP算法。这个算法这里博主也不过多介绍了。但是直接给代码很多小伙伴是很难理解的,而且也不是靠言语一两句话就可以讲明白的。

所以感兴趣的小伙伴可以自己下去了解一下,这里推介一个宝藏UP主的KMP算法课程,UP主很有耐心给大家介绍了该算法,虽然时间有点长,但是我觉得很适合小白来学习这个算法。

———————————————————————————————————————————

附(高质量KMP算法讲解):

【完整版】终于有人讲清楚了KMP算法,Java语言C语言实现_哔哩哔哩_bilibili

二、strtok函数:

1. 介绍:

函数原型:

char * strtok ( char * str, const char * sep );
函数名功能描述头文件
strtok按照指定分隔符对字符串进行切割#include<string.h>

参数说明:

(一)第一个参数str是待分割字符数组的首元素的地址,另外值得一提的是strtok函数会修改str的内容,所以要求str的内容必须是可以被修改的。如果str作为字符指针本身其被const关键字所修饰,亦或者是保存常量字符串首元素地址的指针,那这种传参将不被允许,是一种错误的传参方式。如图所示:

但是当你的源文件以.c作为后缀名的时候,这个传参将会被允许,但也只是表面上的允许,实际运行时会出错。这是由于编译器对于.c为后缀的源文件编译检查不够严格导致的。

(二)第二个参数sep是分隔符的集合,strtok函数允许分隔符有多个。另外sep可以是一个const char*类型的字符指针,也可以是一个字符数组的首元素的地址。

注:怎么理解上面的传参关系:我觉得可以用下面这一句话来总结概括:你可以让有大能力的人做小事,但是你不能让有小能力的或者几乎没有能力的人去做大事。

2. strtok函数的应用场景与工作原理:

应用场景:

strtok函数是用来进行字符串切割的,通过这个函数我们可以对诸如电子邮箱的信息进行一个切割提取,就比如对于一个电子邮箱:zhangsan@year.net,如果我希望能够提取到zhangsan,year,net这样的有用信息的话就可以用这样的函数。

再还有就是在计算机网路里面,对于诸如主机ID——192.168.101.32(底层使用点分十进制方式对无符号整数进行处理的结果)这样的进行一个信息提取我们也能用strtok函数。

再其次就是在密码学里面,对于很长很长的密码报文,我们可以定义一些特别的分割符来对密码报文进行一个切片的处理,以达到化繁为简的目的……

工作原理:

(第一种情况)——str参数不为NULL指针:

strtok函数处理的对象主要是str,也就是第一个参数,如果第一个参数str不是NULL指针,它会根据str的内容,同时对照sep分割符集合里面的分隔符的种类。在发现首个分隔符之后,将其置为'\0',并且记下该分隔符在数组中的位置,做一个标记,为下一轮的分割字符串做准备(后面会证明这一点)。是的,你没听错,这个函数具有记忆功能!!!

之后该函数会返回该标记开始时的位置。注意这里说的是标记开始时的位置,而并非标记本身位置。eg:对于字符串:zhangsan@year.net,如果我们的分隔符sep有“@”和“.”,那对于分隔符“@”而言,它开始时的位置应该是字母z所在的位置。而对于分隔符“.”而言,它开始时的位置应该是字母y所在的位置。

我们这里就以上面的字符串zhannsan@year.net为例,来使用strtok函数对它进行第一次分割,如图所示:

但是也有可能str里面本身就没有sep分隔符里面的任何一个分隔符,那么这个函数将返回str本身。同时标记数组末尾,以表示分割已完成。后面继续分割将返回空指针。

至于如何进行下一轮的分割我们在下面会讲,这里先给大家演示一下str数组里面不存在sep分隔符的话会出现的情况。如图所示:

注:这里的sep数组里面保存的只有空格和"\0"。

(第二种情况)——str参数为NULL指针:

如果第一个参数str用户传过去的是空指针的话,那strtok函数将会从上一次标记的位置开始,去寻找新的分割符。将其置为'\0'并标记该位置。通过这种方式来实现对同一个字符串的第二轮分割。

还是以str = "zhangsan@year.net",sep = "@."为例,我们在进行了第一轮分割的基础之上,进行第二次分割和第三次分割应该这样去传参和使用。如图所示:

之前我们有提到说:该函数具有记忆功能,在这里就得到了很好的一个体现。第一次分割结束之后就记下了@在数组中的位置,第二次分割就从该位置开始往下找新标记,找到.,然后记下.在数组中位置。依次类推,最后会标记在数组末尾,以表示分割已完成。

再继续进行分割则会返回空指针,那是不是这样的呢,我们也可以来测试一下:

大概就是这么一回事,那有小伙伴们可能又要问了,那如果在此之前strtok函数没有标记位置呢。换句话说,strtok函数没有进行哪怕任何一次的有效分割,一上来str参数就传个NULL指针过去会怎么样。这里直接说结论:这个时候会导致程序的崩溃!!!所以在实际使用过程中应当避免这样的行为。

三、strerror函数:

1. 介绍:

函数原型:

char * strerror ( int errnum );
函数名功能描述头文件
strerror打印错误码所对应的错误信息#include<string.h>

返回值说明:返回错误信息字符串的起始地址。

2. 关于错误码:

首先错误码是一个整数,不同的错误码标识程序在运行过程中不同的错误信息。错误码有很多,大概100来条左右。我们可以通过下面这个代码来看一下前十个错误码(0~9)所代表的含义。

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>

int main()
{
	for (int i = 0; i < 10; i++)
	{
		printf("%d.%s\n", i, strerror(i));
	}
	return 0;
}

运行截图:

在实际开发过程中,如果仅仅是用strerror函数来查看错误码所对应的错误信息,这是没有什么实际意义的。实际上每一个程序都有一个全局变量——errno。这个全局变量是保存在头文件#include<string.h>里面(更准确一点来说是在头文件errno.h里面)。保存了当前程序错误码的信息。

当程序状态异常时,编译器会根据当前状态自动更新errno的值,这个全局变量联合strerrno函数来使用可以帮助开发人员更快更准确地确定程序的问题所在。

下面是一个使用示范:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>

int main()
{
	//打开文件:
	FILE* fin = fopen("Unexist.txt", "r");
	if (fin == NULL)
	{
		printf("发生错误:%s", strerror(errno));
		return errno;
	}

	//读文件:
	//......

	//关闭文件:
	fclose(fin);
	fin = NULL;
	return 0;
}

运行截图:

注:上面的代码阅读需要一定的C语言文件操作的基本知识。C语言打开文件的函数是fopen,打开文件的方式则有r,w,a三种方式,分别表示只读,只写和追加。如果用户以只写和追加的方式来打开文件,如果当前文件不存在系统会自动创建文件。而当用户用只读的方式打开文件时,如果文件不存在则会出现错误。

3. perror函数的使用:

提到程序错误信息的打印,就不得不提及一下perror函数了。perror函数是stdio.h标准输入输出流头文件里面的一个标准库函数

函数原型如下所示:

void perror ( const char * str )

该函数的功能是向屏幕打印错误信息,该函数的使用不需要用户对errno这个全局变量有所了解。他会自己检索底层的错误信息后并告诉我们的用户。

除此之外,函数的参数是用户输入的字符常量,这是用户希望打印的自定义信息。之后perror函数向显示器设备打印信息将按照如下格式进行:自定义信息:错误信息这里值得一提的是打印格式中的“冒号”是编译器自己会带上的,不需要用户主动添加!

我们下面使用perror函数来改造上面的代码,以实现和使用strerror函数一样的效果:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>

int main()
{
	//打开文件:
	FILE* fin = fopen("date.txt", "r");
	if (fin == NULL)
	{
		perror("发现错误");
		return errno;
	}

	//读文件:
	//......

	//关闭文件:
	fclose(fin);
	fin = NULL;
	return 0;
}

运行截图:

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

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

相关文章

php 可逆与不可逆加密函数

https://andi.cn/page/621536.html

基于机器学习的锂离子电池容量估计(MATLAB R2021B)

锂离子电池已经广泛应用于电动汽车或混合动力汽车的能源存储装置。由于电化学成分的衰退&#xff0c;锂离子电池随着使用时间的增加&#xff0c;电池性能不断退化&#xff0c;导致电池容量和功率发生衰退。电池容量衰退的因素主要有金属锂沉积&#xff0c;活性物质分解和电解液…

周报(1)<仅供自己学习>

文章目录 一.pytorch学习1.配置GPU2.数据读取问题1&#xff08;已解决问题2&#xff08;已解决 3.卷积的学习 二.NeRF学习1.介绍部分问题1&#xff08;已解决 2.神经辐射场表示问题2&#xff08;已解决问题3&#xff08;已解决问题4&#xff08;已解决问题5&#xff1a;什么是视…

NSSCTF中24网安培训day1中web的题目

我flag呢 直接查看源代码即可CtrlU [SWPUCTF 2021 新生赛]Do_you_know_http 用Burpsuite抓包&#xff0c;之后在User-agent下面添加XFF头&#xff0c;即X-Forwarded-For:127.0.0.1 [SWPUCTF 2022 新生赛]funny_php 首先是php的弱比较&#xff0c;对于num参数&#xff0c;我们…

【ChatGPT】深入解析Prompt提示词及如何高效使用ChatGPT

一、Prompt提示词是什么&#xff1f; 1.1 Prompt的定义 Prompt是人工智能领域中的一个关键概念&#xff0c;尤其在自然语言处理&#xff08;NLP&#xff09;和生成型AI模型中。简而言之&#xff0c;prompt是一段文本或指令&#xff0c;用于引导或启动AI模型的特定响应或操作。…

在pycharm 2023.2.1中运行由R语言编写的ipynb文件

在pycharm 2023.2.1中运行由R语言编写的ipynb文件 背景与目标&#xff1a; 项目中包含由R语言编写的ipynb文件&#xff0c;希望能在pycharm中运行该ipynb文件。 最终实现情况&#xff1a; 未能直接在pycharm中运行该ipynb文件&#xff0c;但是替代的实现方法有&#xff1a;…

SuperCLUE:中文大模型基准测评2024年上半年报告

SuperCLUE是一个中文通用大模型的综合性评测基准&#xff0c;其前身是CLUE&#xff08;The Chinese Language Understanding Evaluation&#xff09;&#xff0c;自2019年成立以来&#xff0c;CLUE基准一直致力于提供科学、客观和中立的语言模型评测。SuperCLUE继承并发展了CLU…

【前端Vue3】——Vue基础知识点总结(万字总结)

&#x1f3bc;个人主页&#xff1a;【Y小夜】 &#x1f60e;作者简介&#xff1a;一位双非学校的大二学生&#xff0c;编程爱好者&#xff0c; 专注于基础和实战分享&#xff0c;欢迎私信咨询&#xff01; &#x1f386;入门知识专栏&#xff1a;&#x1f387;【MySQL&#…

24.7.14(板刷数据结构,警钟长鸣)

上周六&#xff1a; 算是暑假训练第一天&#xff0c;期末考完了真好 cf round951 div2 D cf传送门 之前用正解补过&#xff0c;又臭又长&#xff0c;写完就跑了&#xff0c;这次用哈希补一发 思路&#xff1a;目标字符…

2022睿抗CAIP-编程技能赛-本科组省赛(c++)(未完结)

RC-u1 不要浪费金币 模拟 AC: #include<iostream> #define int long long using namespace std; const int N1e35; int n,m,p[N],ans; signed main(){cin>>n>>m;for(int i1;i<n;i) cin>>p[i];int sum0;for(int i1;i<n;i){if(sump[i]<m) sump…

昇思25天学习打卡营第21天|ResNet50迁移学习

在实际应用场景中&#xff0c;由于训练数据集不足&#xff0c;所以很少有人会从头开始训练整个网络。普遍的做法是&#xff0c;在一个非常大的基础数据集上训练得到一个预训练模型&#xff0c;然后使用该模型来初始化网络的权重参数或作为固定特征提取器应用于特定的任务中。 …

STM32MP135裸机编程:BOOT跳转到APP前关闭所有中断、清除所有中断挂起标志操作方法

0 前言 一般来说&#xff0c;MCU/SOC的BOOT在跳转到APP前都需要进行环境清理的操作&#xff0c;其中必须进行的一项操作便是关闭所有中断、清除所有中断挂起标志。本文介绍基于STM32MP135裸机编程下关闭所有中断、清除所有中断挂起标志的操作方法。 1 操作方法 STM32MP135裸…

缓存与分布式锁

一、缓存 1、缓存使用 为了系统性能的提升&#xff0c;我们一般都会将部分数据放入缓存中&#xff0c;加速访问。 适合放入缓存的数据有&#xff1a; 即时性、数据一致性要求不高的&#xff1b;访问量大且更新频率不高的数据。 在开发中&#xff0c;凡是放入缓存中的数据我们都…

机器人前沿--PalmE:An Embodied Multimodal Language Model 具身多模态大(语言)模型

首先解释这篇工作名称Palm-E&#xff0c;发表时间为2023.03&#xff0c;其中的Palm是谷歌内部在2022.04开发的大语言模型&#xff0c;功能类似ChatGPT&#xff0c;只是由于各种原因没有那样火起来&#xff0c;E是Embodied的首字母&#xff0c;翻译过来就是具身多模态大语言模型…

基于5个K7的多FPGA PCIE总线架构的高性能数据预处理平台

板载FPGA实时处理器&#xff1a;XCKU060-2FFVA15172个QSFP光纤接口&#xff0c;最大支持10Gbps/lane板载DMA控制器&#xff0c;能实现双向DMA高速传输支持x8 PCIE主机接口&#xff0c;系统带宽5GByte/s1个R45自适应千兆以太网口1个FMC子卡扩展接口 基于PCIE总线架构的高性能数据…

c++包管理器

conan conan search&#xff0c;查看网络库 conan profile detect&#xff0c;生成缓存信息conan new cmake_exe/cmake_lib&#xff0c;创建cmakelists.txtconan install .&#xff0c;执行Conanfile.txt中的配置&#xff0c;生成相关的bat文件 项目中配置Conanfile.txt(或者…

【学习笔记】无人机(UAV)在3GPP系统中的增强支持(十一)-无人机服务可用性用例需求

引言 本文是3GPP TR 22.829 V17.1.0技术报告&#xff0c;专注于无人机&#xff08;UAV&#xff09;在3GPP系统中的增强支持。文章提出了多个无人机应用场景&#xff0c;分析了相应的能力要求&#xff0c;并建议了新的服务级别要求和关键性能指标&#xff08;KPIs&#xff09;。…

口袋算法的示例

原理 口袋算法是感知器(Perceptron)算法的一种改进。感知器算法是一种线性分类算法,但在训练数据不是线性可分的情况下,它可能无法收敛,即无法找到一个线性分类器来正确分类所有的训练样本。为了解决这个问题,口袋算法引入了一个"口袋"(Pocket),用来存储迄…

Redis② —— Redis线程模型

1. Redis是单线程吗&#xff1f; 指 接受客户请求 --> 解析请求 --> 进行数据读写操作 --> 发送数据给客户端 这个过程由一个主线程完成redis程序并不是单线程的&#xff0c;在启动时会启动后台进程 2.6之前启动两个后台线程&#xff0c;分别处理关闭文件、AOF刷盘4.…

Go 语言返回组装数据

文章id 文章标题 ..... 分类 字段 &#xff1a;[分类名&#xff0c;分类描述 .... ]标签字段 : [标签名, 标签id ..... ]type ArticleWithCategoryLabel struct {system.SysArticleCategoryName system.SysCategorie json:"category_name"LabelName system.SysLab…