学习笔记6:字符串库函数(下)

news2025/1/8 18:11:00

目录

一. strstr模拟实现

二. strtok模拟实现

三.关于strerror和perror的说明


一. strstr模拟实现

库函数strstr函数首部:char * strstr ( const char *str1, const char * str2);

函数的功能是在str1指向的主字符串中寻找子串str2,并且返回主字符串中子字符串第一次出现的位置(主字符串中第一个子字符串的首地址)。

如果主字符串中找不到子字符串则函数返回空指针。

函数的实现思路是暴力遍历法:

用两层循环来实现,外层循环用一个循环变量i遍历主字符串str1,每当在主字符串中找到子字符串的首元素就进入第二层循环进行两个字符串的匹配,若匹配失败,指针i回溯到匹配的起始位置继续寻找下一个子串首字符,重复上述步骤。

内层循环以两个字符串的终止符或不相等的对应字符为结束标志。

匹配成功的标志是内层循环维护子串str2的指针指向子串str2的终止符。

模拟实现:(这里使用str1和str2的指针运算代替下标i和j的运算)

char* my_strstr(const char* str1, const char* str2)
{
	assert(str1 && str2);
	if (!(*str2))                     如果子串为空字符串则返回主串的首地址
	{
		return (char*)str1;
	}
	const char*  RE1 = NULL;          用于记录str1指针回溯的位置
	const char*  RE2 = str2;          用于记录str2指针回溯的位置
	while (*str1)                     找到主串中的\0则停止循环
	{
		RE1 = str1;
		while (*str1++ == *str2++)
		{
			if (!(*str2))             匹配子串中的\0则代表在主串中找到了子串
			{
				return (char*)RE1;
			}
			if (!(str1))              匹配过程中找到主串的\0则函数直接返回NULL
			{
				return NULL;
			}
		}
		str1 = RE1;
		str2 = RE2;                   str1,str2指针回溯
		str1++;
	}
	return NULL;
}

另外一种可读性更好,思路更加清楚的写法:

char* my_strstr2(const char* str1, const char* str2)
{
	assert(str1 && str2);
	if (!(*str2))                     如果子串为空字符串则返回主串的首地址
	{
		return (char*)str1;
	}
	const char* pstr2 = str2;         pstr1和pstr2用于内层循环进行字符串匹配
	const char* pstr1 = str1;
	while (*str1)
	{
		pstr1 = str1;                 令pstr1和pstr2指向进行字符串匹配的起始位置
		pstr2 = str2; 
		while (*pstr1 && (*pstr1 == *pstr2))
		{                             找到匹配过程中的终止字符或不相等的对应字符后跳出循环
			pstr1++;
			pstr2++;
		    if ('\0' ==*pstr2 )        只有在匹配过程中pstr2指向子字符串终止符才算匹配成功
		    {
			    return (char*)str1;
		    }
		}
		str1++;
	}
	return NULL;
}                                     此种写法很容易进行越界检查和思路梳理

测试代码:

int main()
{
	char arr1[] = "acdsdssdsfdgdh";
	char arr2[] = "fdg";
	char* retlib = strstr(arr1, arr2);
	char* retmy = my_strstr(arr1, arr2);
	char* retmy2 = my_strstr2(arr1, arr2);
	if (retlib || retmy || retmy2)
	{
		printf("%s\n", retlib);
		printf("%s\n", retmy);
		printf("%s\n", retmy2);
	}
	return 0;
}

二. strtok模拟实现

库函数strtok函数首部: char * strtok ( char * str, const char * sep );

1.sep参数是个字符串,定义了用作分隔符的字符集合;

2.第一个参数指定一个字符串,它包含了0个或者多个sep字符串中的字符作为分割标记;

3.strtok函数找到str中的分割标记(sep字符串中的字符),并将其用 \0 替换,返回一个指向这个分割出来的子串的指针(注:strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可修改);


4.strtok函数的第一个参数不为 NULL ,函数将找到str中第一个分割标记,strtok函数将保存它在字符串中的位置


5.strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标记;


6.如果字符串中不存在更多的标记,则返回 NULL 指针;

 

模拟实现strtok: 

char* my_strtok(char* String, const char* label)
{
	assert(label);
	static flag = 1;           用于标记被分割的主串是否被完全遍历
	static char* Mark = NULL;  用于记录被分割主串中的分割位置
	if (flag && !String)       第一次传空指针则返回空指针,以及后续主串若已经被完全遍历再次调 
                               用该函数时则返回空指针 
                               
	{
		return NULL;   
	}
	else if (String)               传入的String为非空指针时(传入新的被分割主串)
	{
		flag = 0;                      重置flag
		Mark = NULL;                   重置Mark
		char* ret = String;            记录分割段的起始位置
		const char* plabel = label;    plabel用于遍历分隔符字符串
		while (*String)
		{
			plabel = label;
			while (*plabel && *plabel != *String)
			{
				plabel++;
			}
			if (*plabel)          若在主串中找到分割符,则完成分割操作
			{
				*String = '\0';
				Mark = String;
				return ret;
			}
			String++;
		}
		flag = 1;                  从这里跳出循环代表主串已经被完全遍历,flag标记为1
		return ret;
	}
	else
	{
		char* pString = Mark+1;       用pSring作为继续遍历主串的指针变量
		char* ret = Mark + 1;         记录分割段的起始位置
		const char* plabel = label;   plabel用于遍历分隔符字符串
		while (*pString)
		{
			plabel = label;
			while (*plabel && *plabel != *pString)
			{
				plabel++;
			}
			if (*plabel)         若在主串中找到分割符,则完成分割操作
			{
				*pString = '\0';
				Mark = pString;
				return ret;
			}
			pString++;
		}
		flag = 1;                从这里跳出循环代表主串已经被完全遍历,flag标记为1
		return ret;
	}
}

测试代码: 

int main()
{
	char str1[] = "sdf@cc.gif@bit";
	char str2[] = "sdf@cc.gif@bit";
	char str3[] = "sdf@cc.gif@bit";
	char str4[] = "sdf@cc.gif@bit";
	char label[] = "@.";
	char* pstr = NULL;
	for (pstr = strtok(str1, label); pstr != NULL; pstr = strtok(NULL, label))
	{
		printf("%s\n", pstr);
	}
	printf("-----------\n");
	for (pstr = my_strtok(str2, label); pstr != NULL; pstr = my_strtok(NULL, label))
	{
		printf("%s\n", pstr);
	}
	printf("-----------\n");
	for (pstr = strtok(str3, label); pstr != NULL; pstr = strtok(NULL, label))
	{
		printf("%s\n", pstr);
	}
	printf("-----------\n");
	for (pstr = my_strtok(str4, label); pstr != NULL; pstr = my_strtok(NULL, label))
	{
		printf("%s\n", pstr);
	}
	printf("-----------\n");
	return 0;
}

三.关于strerror和perror的说明

sterror函数首部:char * strerror ( int errnum );
C语言标准库中有一个全局整形变量errno,当用户调用库函数发生错误时,库函数会将错误码(一个整形数值)存在errno中,将errno作为实参传入strerror函数中就可以将错误码(一个整形数值)翻译为对应的错误信息,并以字符串首地址的形式返回给用户,用户可以将描述错误信息的字符串打印出来。

比如打开文件时,用于检查是否发生错误可以调用该函数。

#include <stdio.h>
#include <string.h>
#include <errno.h>//必须包含的头文件
int main ()
{
    FILE * pFile;
    pFile = fopen ("unexist.ent","r");
    if (pFile == NULL) 
    {
       printf ("Error opening file unexist.ent: %s\n",strerror(errno));
    }
    //errno: Last error number
    return 0;
}

perror函数首部:void perror( const char *string ) ;

形参中的const char *string是用户传入的自定义信息,perror函数可以自动访问全局变量errno,并将错误信息和用户传入的自定义信息一起打印出来,使用起来更加方便。

比如打开文件时,用于检查是否发生错误也可以调用该函数。

#include <stdio.h>
#include <string.h>
#include <errno.h>//必须包含的头文件
int main ()
{
    FILE * pFile;
    pFile = fopen ("unexist.ent","r");
    if (pFile == NULL) 
    {
       perror("error message :");
    }
    return 0;
}

 

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

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

相关文章

JS数组对象——英文按照首字母进行排序sort()、localeCompare()

JS数组对象——英文按照首字母进行排序(sort、localeCompare&#xff09;上期回顾场景复现sort()方法与localeCompare实例应用上期回顾 文章内容文章链接JS数组对象——根据日期进行排序Date.parse()&#xff0c;按照时间进行升序或降序排序https://blog.csdn.net/XSL_HR/arti…

【CANN训练营第三季】AI目标属性编辑应用

文章目录1、参考样例进行运行stargan2、dvpp媒体数据处理结业考核题目1、题目2、题目31、参考样例进行运行stargan 下载stargan后&#xff0c;查看readme&#xff0c;进行复现。 # 为了方便下载&#xff0c;在这里直接给出原始模型下载及模型转换命令,可以直接拷贝执行。 cd …

Tic-Tac-Toe:基于Minimax算法的人机对弈程序(python实现)

目录 1. 前言 2. Minimax算法介绍 2.1 博弈树 2.2 估值函数 2.3 基本算法思想 2.4 实例1 ​​​​​​​2.5 实例2—棋类游戏 2.6 小结 3. Tic-Tac-Toe minimax AI实现 3.1 函数说明 3.2 处理流程 3.3 代码 4. 小结 1. 前言 在上一篇中实现一个简单的Tic-Tac-Toe人…

【07】概率图推断之信念传播

概率图推断之信念传播 文章目录将变量消除视为信息传递信息传递算法加总乘积信息传递因子树上的加总乘积信息传递最大乘积信息传递总结在《概率图推断之变量消除算法》中&#xff0c;我们讲了变量消除算法如何对有向图和无向图求P(Y∣Ee)P(Y \mid E e)P(Y∣Ee)的边缘概率。 …

java 微服务之MQ 异步通信

初识MQ 同步调用存在的问题 异步调用常见实现就是事件驱动模式 事件驱动模式优势&#xff1a; 优势1&#xff1a;服务解耦 一旦有新业务只需要订阅或者减少事件就行了 优势2&#xff1a;性能提升&#xff0c;吞吐量提高 优势3&#xff1a;服务没有强依赖&#xff0c;不用担…

【自学C++】C++注释

C注释 C注释教程 用于注解说明解释程序的文字就是注释&#xff0c;注释提高了代码的阅读性。同时&#xff0c;注释也是一个程序员必须要具有的良好编程习惯。我们应该首先将自己的思想通过注释先整理出来&#xff0c;再用代码去体现。 在 C 中&#xff0c;一旦程序中某部分内…

数据结构和算法-计数排序

1.算法描述 技术排序是一个基于比较的排序算法&#xff0c;该算法于1954由Harold H. Seward 提出。它的优势在于对 一定范围内的整数排序时&#xff0c;它的复杂度为O&#xff08;nk&#xff09;&#xff08;其中k是整数的范围&#xff09;&#xff0c;快于任何比较排序算 法…

JavaEE高阶---Spring事务和事务传播机制

一&#xff1a;什么是事务&#xff1f; 事务定义&#xff1a;将⼀组操作封装成⼀个执⾏单元&#xff08;封装到⼀起&#xff09;&#xff0c;要么全部成功&#xff0c;要么全部失败。 二&#xff1a;Spring中事务的实现 编程式事务&#xff08;⼿动写代码操作事务&#xff09…

使用 Flink CDC 实现 MySQL 数据实时入 Apache Doris

简介 主要内容如下&#xff1a; MySQL 安装和开启binogFlink环境准备Apache Doris 环境准备启动Flink CDC作业 1. MySQL 安装和开启binog 参考文章&#xff1a;Ubuntu 安装 Mysql server, 这篇文章介绍了MySQL的安装&#xff0c;用户创建&#xff0c;Binlog开启等内容。 M…

Linux基础入门和常用命令

Linux基础入门和常用命令一. Linux介绍1.1 Linux的发行版本二. Linux环境搭建三. Linux的常用指令3.1 Linux下的目录结构3.2 ls命令3.3 pwd命令3.4 cd指令3.5 touch指令3.6 mkdir指令3.7 rmdir指令和 rm 指令3.8 man指令3.9 mv指令3.10 cp指令3.11 cat3.12 more指令3.13 less指…

基于机器学习组合模型的个人信用评估

《基于机器学习组合模型的个人信用评估》课程报告 摘要 个人信用评估在信用经济市场发挥着及其重要的基础作用&#xff0c;促进信用经济的发展&#xff0c;稳定经济市场。个人信用信息主要有个人基本信息、还款能力和还款意愿;个人基本信息主要由年龄、性别、地区等特征构成&…

C语言灵魂核心——指针深度修炼

&#x1f412;个人主页&#xff1a;平凡的小苏&#x1f4da;学习格言&#xff1a;别人可以拷贝我的模式&#xff0c;但不能拷贝我不断往前的激情目录 1. 字符指针 2. 指针数组 3. 数组指针 3.1 数组指针的定义 3.2 &数组名VS数组名 3.3 数组指针的使用 4. 数组参数、…

【读论文】TCPMFNet

【读论文】TCPMFNet简单介绍网络结构编码器图像融合网络Vision Transformer特征融合网络网格连接解码器损失函数总结参考论文&#xff1a;https://www.sciencedirect.com/science/article/pii/S1350449522003863 如有侵权请联系博主 简单介绍 今天要介绍的是TCPMFNet&#xf…

六大排序算法

1. 插入排序步骤&#xff1a;1.从第一个元素开始&#xff0c;该元素可以认为已经被排序2.取下一个元素tem&#xff0c;从已排序的元素序列从后往前扫描3.如果该元素大于tem&#xff0c;则将该元素移到下一位4.重复步骤3&#xff0c;直到找到已排序元素中小于等于tem的元素5.tem…

如何使用 Pandas 清洗二手房数据并存储文件

目录 一、实战场景 二、知识点 python 基础语法 python 文件读写 pandas 数据清洗 三、菜鸟实战 清洗前的文件 读取源文件 对二手房数据进行清洗 清洗完成后保存到文件 运行结果 运行截图 结果文件 一、实战场景 如何使用 Pandas 清洗的二手房数据并存储文件 二…

初识结构体(详细版)

目录 一、结构体的声明 1、结构的基础知识 2、结构的声明 3、结构成员的类型 4、结构体变量的定义和初始化 二、结构体成员的访问 1、点操作符访问 2、->操作符访问 3、解引用访问 三、结构体嵌套 四、结构体传参 1、传值调用 2、传址调用 一、结构体的声明 1、结构的基…

Vue2前端路由(vue-router的使用)

一、vue2axiosExpressMySQL实现前后端交互1、后台&#xff1a;&#xff08;1&#xff09;确定MySQL的表格&#xff1a;明确数据库 &#xff08;mvc&#xff09; —- 数据表(ssm_book)&#xff08;2&#xff09;创建Express项目&#xff1a;mysql2、cors、Sequelize(ORM)、nodem…

imx6ull Linux sdk下载验证

本文章是基于整点原子的imx6ull alpha开发板一.Linux SDK源码以及image1.环境准备其他的工具我们就不做介绍了&#xff0c;比如ubuntu ftp,ssh等等&#xff0c;我们主要来介绍下编译链1.1 交叉编译链背景&#xff1a;因为在原子的教程中有强调最新的Linaro gcc编译完uboot后无法…

备受认可!中睿天下荣登“2022创业邦100未来独角兽”年度榜单

近日&#xff0c;由创业邦、复旦大学管理学院主办的2022创业邦100未来独角兽峰会暨创业邦年会在上海举办。在峰会现场&#xff0c;2022创业邦100未来独角兽榜单正式揭晓&#xff0c;中睿天下凭借出众的综合实力荣登榜单。作为一家以“实战对抗”为特点的能力价值型网络安全厂商…

Java版设计模式/设计模式的作用是什么/类之间有哪些关系?又怎么表示

继续整理记录这段时间来的收获&#xff0c;详细代码可在我的Gitee仓库SpringBoot克隆下载学习使用&#xff01; 1. 设计模式概述 1.1 设计模式创始“4人组” ErichGamma–艾瑞克伽马Richard Helm—理查德赫尔码Ralph Johnson----拉尔夫约翰逊John Vlissides—约翰威力斯蒂斯…