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

在#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;
}

运行截图:

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

数据结构——查找(线性表的查找与树表的查找)

目录 1.查找 1.查找的基本概念 1.在哪里找&#xff1f; 2.什么查找&#xff1f; 3.查找成功与否&#xff1f; 4.查找的目的是什么&#xff1f; 5.查找表怎么分类&#xff1f; 6.如何评价查找算法&#xff1f; 7.查找的过程中我们要研究什么&#xff1f; 2.线性表…

Databricks中的DBFS(Databricks File System)和对象存储(Object Storage)

Whats DBFS and ObjectStorage 在Databricks中&#xff0c;DBFS&#xff08;Databricks File System&#xff09;和对象存储&#xff08;如Amazon S3、Azure Blob Storage等&#xff09;是两种主要的数据存储选项。它们在数据存储和访问方面各有特点&#xff1a; DBFS Storage…

【Python】数据处理(mongodb、布隆过滤器、索引)

数据 数据预处理 df pd.read_csv(file_path, encodingANSI) csv的编码方式一定要用 ANSI。要不然会出现各种报错 import pandas as pd from datetime import datetime# 读取CSV文件 file_path book_douban.csv df pd.read_csv(file_path, encodingANSI)# 定义一个函数来…

excel有条件提取单元格特定文本(筛选纯文字的单元格或含有数字的单元格、单元格提取不同的文本长度)

实际工作背景 需要对导出的银行流水中的数十个村以及对应的村小组进行分组统计&#xff0c;但是初始的表格中村和小组是混在一起的&#xff0c;如下图所示&#xff1a; 目的&#xff1a;将大树村和大树村小组名称分别筛选出来 1.观察发现&#xff0c;大树村小组的单元格第4…

3 C 语言运算符深度解析:从基础到实战

目录 1 运算符分类 2 算术运算符与算术表达式 2.1 算术运算符的用法 2.2 左操作数和右操作数 3 关系运算符与关系表达式 3.1 关系运算符的用法 3.2 常量左置防错 3.3 三数相等判断误区 4 逻辑运算符与逻辑表达式 4.1 逻辑运算符的用法 4.2 闰年的判断 4.3 短路运算…

AI大模型探索之旅:深潜大语言模型的训练秘境

在人工智能的浩瀚星空中&#xff0c;大语言模型无疑是最耀眼的星辰之一&#xff0c;它们以无与伦比的语言理解与生成能力&#xff0c;引领着智能交互的新纪元。本文将带您踏上一场探索之旅&#xff0c;深入大语言模型的训练秘境&#xff0c;揭开其背后复杂而精妙的全景画卷。 …

51单片机9(使用左移实现流水灯编程)

一、序言&#xff1a;下面我们来给大家介绍一下这个流水灯&#xff0c;流水灯如何来实现&#xff1f;我们依然使用这个工程来完成它。 1、那要使用实现这个流水灯&#xff0c;那我们只需要让D1到D8逐个的点亮&#xff0c;那同样要实现它足够的点亮&#xff0c;也会涉及到延时&…

PNC103/103B-PORPSVOC/波洛斯数据手册高性能32位音频处理器

PNC 103/103B是针对降噪市场推出的一颗音频处理芯片&#xff0c;搭载POROSVOC业内领先的DNN神经网络降噪算法或搭载POROSVOC AECAES回声消除算法&#xff0c;可广泛且快速应用于音视频&#xff0c;对讲&#xff0c;话务等领域。 该芯片采用32bit RSIC架构内核&#xff0c;并加入…

【学习笔记】无人机(UAV)在3GPP系统中的增强支持(一)-3GPP TR 22.829 V17.1.0技术报告

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

MyBatis where标签替换WHERE 1 = 1会提升性能吗

MyBatis <where>标签替换WHERE 1 1会提升性能吗 查看项目早期数据库查询语句时&#xff0c;发现很多地方写了WHERE 1 1&#xff0c;怀疑这里有性能损失&#xff0c;想替换成<where>标签。 验证 已知索引 CREATE INDEX BP_LOG_BP_DATE_IDX ON QXX.BP_LOG (BP_…

pxe高效网络批量装机

文章目录 一&#xff0c; PXE远程安装服务&#xff08;一&#xff09;三种系统装机的方式&#xff08;二&#xff09;linux装机1. 加载 Boot Loader2. 加载启动安装菜单3. 加载内核和 initrd4. 加载根文件系统5. 运行 Anaconda 安装向导 &#xff08;三&#xff09;实现过程&am…

games103作业2(未完)

PBD方法 首先是每个质点的力的分析&#xff0c;不考虑碰撞和弹簧弹力的情况下&#xff0c;每个质点受重力的影响&#xff0c;所以需要对每个质点进行速度和位置的重力影响更新。 float t 0.0333f; float damping 0.99f; int[] E; float[] L; Vector3[] V; Vector3 gra…

Ubuntu系统安装mysql之后进行远程连接

1.首先要配置数据库允许进行远程连接 1.1 打开MySQL配置文件 /etc/mysql/mysql.conf.d/mysqld.cnf sudo vim /etc/mysql/mysql.conf.d/mysqld.cnf1.2 修改 bind-address 行 #按i进入插入模式 bind-address 0.0.0.0 #按 Esc 键退出插入模式。 #输入:wq 然后按 Enter 保存并退…

【机器翻译】基于术语词典干预的机器翻译挑战赛

文章目录 一、赛题链接二、安装库1.spacy2.torch_text 三、数据预处理赛题数据类定义 TranslationDataset批量处理函数 collate_fn 四、编码器和解码器Encoder 类Decoder 类Seq2Seq 类注意事项 五、主函数1. load_terminology_dictionary(dict_file)2. train(model, iterator, …

windows USB 设备驱动开发- USB Type-C支持(二)

Microsoft 提供 USB Type-C 连接器系统软件接口 (UCSI) 符合规范的 ACPI 传输驱动程序。 如果你的设计包含带有 ACPI 传输的嵌入式控制器&#xff0c;请在系统的 BIOS/EC 中实现 UCSI&#xff0c;并加载随机 UCSI 驱动程序&#xff08;UcmUcsiCx.sys 和 UcmUcsiAcpiClient.sys&…

【Linux】:重定向和缓冲区

朋友们、伙计们&#xff0c;我们又见面了&#xff0c;本期来给大家带来关于重定向和缓冲区的相关知识点&#xff0c;如果看完之后对你有一定的启发&#xff0c;那么请留下你的三连&#xff0c;祝大家心想事成&#xff01; C 语 言 专 栏&#xff1a;C语言&#xff1a;从入门到精…

海事无人机解决方案

海事巡察 海事巡察现状 巡查效率低下&#xff0c;存在视野盲区&#xff0c;耗时长&#xff0c;人力成本高。 海事的职能 统一管理水上交通安全和防治船舶污染。 管理通航秩序、通航环境。负责水域的划定和监督管理&#xff0c;维护水 上交通秩序&#xff1b;核定船舶靠泊安…

Spring Boot集成groovy快速入门Demo

1.什么是groovy&#xff1f; Groovy 是构建在 JVM 上的一个轻量级却强大的动态语言&#xff0c;它结合了 Python、Ruby 和 Smalltalk 的许多强大的特性。 Groovy 就是用 Java 写的&#xff0c;Groovy 语法与 Java 语法类似&#xff0c;Groovy 代码能够与 Java 代码很好地结合&…

QQ频道导航退出

若该文为原创文章&#xff0c;转载请注明原文出处 本文章博客地址&#xff1a;https://hpzwl.blog.csdn.net/article/details/140413538 长沙红胖子Qt&#xff08;长沙创微智科&#xff09;博文大全&#xff1a;开发技术集合&#xff08;包含Qt实用技术、树莓派、三维、OpenCV…

C#中的MD5摘要算法与哈希算法

文章目录 一、哈希算法基础二、MD5 算法原理三、MD5摘要算法四、哈希算法五、C#实现示例MD5算法示例哈希算法示例字符串MD5值对比 六、总结 一、哈希算法基础 哈希算法是一种单向密码体制&#xff0c;它将任意长度的数据转换成固定长度的字符串。这种转换是不可逆的&#xff0…