【C语言】——字符串函数的使用与模拟实现(下)
【C语言】——字符串函数的使用与模拟实现(下)
- 前言
- 五、长度受限类字符串函数
- 六、 s t r s t r strstr strstr 函数
- 6.1、函数功能
- 6.2、函数的使用
- 6.3、函数的模拟实现
- (1)解题思路
- (2)代码实现
- 七、 s t r t o k strtok strtok 函数
- 7.1、函数功能
- 7.2、函数的使用
- 八、 s t r e r r o r strerror strerror 函数
- 8.1、函数功能
- 8.2、函数的使用
- 九、 p e r r o r perror perror 函数
前言
上一篇文章:【C语言】——字符串函数的使用与模拟实现(上)中,我曾详细介绍了: s t r s t r strstr strstr、 s t r c p y strcpy strcpy、 s t r c a t strcat strcat、 s t r c m p strcmp strcmp 这四个函数及其模拟实现,接下来,让我们继续学习字符串相关的函数吧。
五、长度受限类字符串函数
5.1、 s t r n c p y strncpy strncpy 函数
功能:将源字符串的前 n u m num num 个字符拷贝到目标字符串中
- 如果源字符串长度小于 n u m num num,则拷贝完源字符创后,在目标空间后面
追加\0,直到 n u m num num 个。 - 如果源字符串长度大于 n u m num num,则在拷贝完源字符串前 n u m num num 个字符后,末尾
不会自动补 ‘\0’
5.2、 s t r n c a t strncat strncat 函数
功能:将源字符串的前 n u m num num个字符追加至目标字符串的末尾
- 如果源字符串长度大于 n u m num num,追加前 n u m num num 个字符并在后面
补'\0' - 如果源字符串长度小于 n u m num num,追加整个源字符串并
补'\0'。(只补一个,不会一直补到 n u m num num 个)
5.3、 s t r n c m p strncmp strncmp 函数
功能:比较 s t r 1 str1 str1 和 s t r 2 str2 str2 的前 n u m num num 个字符,如果相等就继续往后比较,最多比较 n u m num num 个字符,如果提前发现不一样,提前结束,大的字符所在的字符串大于另外一个,如果 n u m num num 个字符都相等,就返回 0。
六、 s t r s t r strstr strstr 函数
6.1、函数功能
功能:查找字符串 s t r 1 str1 str1 中是否包含字符串 s t r 2 str2 str2 的内容,如果有,返回第一次出现的地址,如果没有,返回空指针。
需要注意的是:字符串的比较匹配不包含 ‘\0’,以 ‘\0’ 作为结束标志。
6.2、函数的使用
#include<stdio.h>
#include<string.h>int main()
{char str1[] = "This is a simple string";char* str2;str2 = strstr(str1, "simple");printf("%s\n", str2);for (int i = 0; i < strlen("simple"); i++){printf("%c", str2[i]);}printf("\n");str2 = strstr(str1, "are");printf("%s\n", str2);return 0;
}
运行结果:

6.3、函数的模拟实现
(1)解题思路
( i ) (i) (i) 首先,我们来看最简单的情况:在 “ a b c d e f abcdef abcdef” 中找 “ c d e cde cde”
- 我们先来看上左图: s t r 1 str1 str1 和 s t r 2 str2 str2 指向两个字符串起始位置,是我们传入的值,这里我额外创建指针 c p cp cp 变量,用指针 c p cp cp 来遍历 s t r 1 str1 str1 数组。
- 看上右图:当指针 c p cp cp 指向字符 ‘ c c c’ 时,发现字符 ‘ c c c’ 与 s t r 2 str2 str2的首字符相同。
为进一步确认, c p cp cp 与 s t r 2 str2 str2同时遍历后面的字符,每遍历一次,相比较一次。当 s t r 2 str2 str2 遍历完字符串,比较结束,发现所比较的字符全部相等,即找到 s t r 1 str1 str1 中的 s t r 2 str2 str2。
上面的思路看起来是可行的,但是还是存在这很大的问题:
- 函数要求的是返回地址,也就是返回上面 ‘ c c c’ 字符的地址,可是 c p cp cp 指针跑后面去了,怎么返回他的地址呢?
- 想象一下,当 c p cp cp 开始比较时,与 s t r 2 str2 str2 的前几个字符是相等的,但后面不相等,这时是不是
又要从‘c’的下一个字符重新开始比较,但是 c p cp cp 与 s t r 2 str2 str2都跑后面去了,还怎么找到下一个字符呢?
( i i ) (ii) (ii) 下面,我们来考虑复杂一点的情况:在 “ a b b b c d f abbbcdf abbbcdf” 中找 “ b b c bbc bbc”
鉴于上述情况出现的两种问题,我们再额外创建两个指针变量 s 1 s1 s1 与 s 2 s2 s2,他们负责实现字符的逐个比较,而 c p cp cp 与 s t r 2 str2 str2用于记录开始比较的起始位置。这样,当 s 1 s1 s1 与 s 2 s2 s2 只有部分相等时,依然可以找到下一个字符重新开始比较
( i i i ) (iii) (iii) 再看最后一种情况:如果找不到怎么办?例如:在 “ a b b b c abbbc abbbc” 中找 “ b b c c bbcc bbcc”
其实,中不到的情况的解决思路与第二种情况时一样的,不同的是,当 c p cp cp 遍历完整个 s t r 1 str1 str1 字符串(即 c p cp cp 指向 \0)还没找到时,即 s t r 1 str1 str1 中不存在 s t r 2 str2 str2 字符串,返回空指针
(2)代码实现
char* my_strstr(const char* str1, const char* str2)
{//创建指针变量:cp、s1、s2//因为并不知道什么时候开始比较,s1、s2先置空const char* cp = str1;const char* s1 = NULL;const char* s2 = NULL;//特殊情况:当str2为空时,返回整个str1if (str2 == NULL){return (char*)str1;}//循环,当cp指向‘\0’时,退出,表面没找到while (*cp){//进入逐个比较if (*cp == *str2){//给s1、s2赋值,让他们进行比较s1 = cp;s2 = str2;//如果他们相等,并且不等于‘\0’比较继续while (*s1 != '\0' && *s2 != '\0' && *s1 == *s2){s1++;s2++;}//退出比较时,表明遇到不一样的字符//这时有两种情况//退出后,当s2指向‘\0’时,表明成功找到//当s2不是指向‘\0’,表示并不是要找的字符串if (*s2 == '\0'){return (char*)cp;}}cp++;}//整个循环结束,表明没找到,返回空指针return NULL;}
七、 s t r t o k strtok strtok 函数
7.1、函数功能
函数功能:将 s t r str str 字符串 以字符 d e l i m i t e r s delimiters delimiters 作为分隔符,分割成若干段。
- s t r str str 参数表示
要分割的字符串 - d e l i m i t e r s delimiters delimiters 参数表示
分隔符,分隔符可以是多个 - s t r t o k strtok strtok 函数找到 s t r str str 中的下一个标记,并将其用 \0 结尾,返回一个
指向这段字符串的指针 - s t r t o k strtok strtok 函数会
改变被操作的字符串,所以在使用 s t r t o k strtok strtok 函数切分的字符串一般都是临时拷贝的内容,并且可以被修改 - s t r t o k strtok strtok 函数的第一个参数不为NULL时,函数将
找到s t r str str 中的第一个标记, s t r t o k strtok strtok 函数将保存它在字符串中的位置 - s t r t o k strtok strtok 函数的第一个参数为NULL,函数将在同一个字符串中
被保存的位置开始,查找下一个标记 - 如果字符串
不存在更多的标记,则返回NULL指针
7.2、函数的使用
像 happy@qq.com,现在我想将 happy、qq、com 这三段单独提取出来,这时就可以使用 s t r t o k strtok strtok函数
#include<stdio.h>
#include<string.h>int main()
{char arr1[] = "happy@qq.com";char arr2[30];const char* sep = "@.";strcpy(arr2, arr1);char* ret = NULL;ret = strtok(arr2, sep);printf("%s\n", ret);ret = strtok(NULL, sep);printf("%s\n", ret);ret = strtok(NULL, sep);printf("%s\n", ret);ret = strtok(NULL, sep);printf("%s\n", ret);return 0;
}
运行结果:

可以看到, s t r t o k strtok strtok 函数很奇怪,只有第一次调用他是传的是要切割的字符串 a r r 2 arr2 arr2,之后调用都是传 NULL。
这是因为该函数每一次调用该函数都把当前找的这个标记的结束位置记住,这样下次再调用该函数就能从上次保存的位置开始找下一个标记
当第四次调用该函数时,因为已经找到结束位置,因此函数返回空指针。
注:这里只是示范,实际上是不允许这样打印的
当然,上面的代码有很大的改进空间,因为我们往往不知道有几个分隔符,这时我们可以用一种巧妙的写法
#include<stdio.h>
#include<string.h>int main()
{char arr1[] = "happy@qq.com@happy@qq.com";char arr2[30];const char* sep = "@.";strcpy(arr2, arr1);char* ret = NULL;for (ret = strtok(arr2, sep); ret != NULL; ret = strtok(NULL, sep)){printf("%s\n", ret);}return 0;
}
我们来分析一下for (ret = strtok(arr2, sep); ret != NULL; ret = strtok(NULL, sep))这个循环:
- 首先是初始化部分
ret = strtok(arr2, sep):将strtok(arr2, sep)的值传给 r e t ret ret 变量 - 接着是判断部分
ret != NULL:当 r e t ret ret 不为空时,循环继续,即打印该段字符串;当返回值为空,表示已经分割完字符串,循环结束 - 最后是调整部分
ret = strtok(NULL, sep):将strtok(NULL, sep)的返回值赋给 r e t ret ret 变量 - 这个代码巧妙在 s t r t o k strtok strtok 函数要求第一次传的是字符串,而之后传的是NULL,这里完美运用了 f o r for for 循环的特点:
先初始化,并且初始化只执行一次,调整语句执行多次。
运行结果:

八、 s t r e r r o r strerror strerror 函数
8.1、函数功能
功能:把参数部分错误码对应的错误信息的字符串地址返回来
在不同的系统和 C 语言标准库中都规定了一些错误码,一般放在 < e r r n o . h errno.h errno.h> 这个头文件中说明。
C语言程序启动时就会使用一个全面的变量 e r r n o errno errno 来记录程序当前的错误码,只不过程序启动时 e r r n o errno errno 是 0,表示没有错误。
当我们在使用标准库中的函数的时候发生了某种错误,就会将对应的错误码,放在errno中,而一个错误码的数字是整数,因此很难理解是什么意思,所以每个错误码都是有对应的错误信息的。 s t r e r r o r strerror strerror 函数就可以将错误对应的错误信息字符串的地址返回。
8.2、函数的使用
我们打印 0 − 10 0-10 0−10 这些错误码所对应的信息
#include<errno.h>
#include<string.h>
#include<stdio.h>int main()
{int i = 0;for (i = 0; i <= 10; i++){printf("%s\n", strerror(i));}return 0;
}
运行结果:

再比如:我们以读的方式打开一个不存在的文件,让其返回错误信息
#include<errno.h>
#include<string.h>
#include<stdio.h>int main()
{FILE* pFile;pFile = fopen("unexist.ent", "r");if (NULL == pFile){printf("Error opening file unexist.ent: %s\n", strerror(errno));}return 0;
}
运行结果:

九、 p e r r o r perror perror 函数
功能: p e r r o r perror perror 函数相当于 p e i n t f + s t r e r r o r peintf+strerror peintf+strerror,它会打印完参数本分的字符串内容后,再打印一个冒号和一个空格,再打印错误信息。
例如:
#include<errno.h>
#include<string.h>
#include<stdio.h>int main()
{FILE* pFile;pFile = fopen("unexist.ent", "r");if (NULL == pFile){perror("Error opening file unexist.ent");}return 0;
}
运行结果:

好啦,本期关于部分字符串函数的介绍及模拟实现就介绍到这里啦,希望本期博客能对你有所帮助,更多关于字符串的函数还请收看下一期。同时,如果有错误的地方请多多指正,让我们在C语言的学习路上一起进步!
相关文章:
【C语言】——字符串函数的使用与模拟实现(下)
【C语言】——字符串函数的使用与模拟实现(下) 前言五、长度受限类字符串函数5.1、 s t r n c p y strncpy strncpy 函数5.2、 s t r n c a t strncat strncat 函数5.3、 s t r n c m p strncmp strncmp 函数 六、 s t r s t r strstr strstr 函数6.1、函…...
mac安装nvm详细教程
0. 前提 清除电脑上原有的node (没有装过的可以忽略)1、首先查看电脑上是否安装的有node,查看node版本node -v2、如果有node就彻底删除nodesudo rm -rf /usr/local/{bin/{node,npm},lib/node_modules/npm,lib/node,share/man/*/node.*}2、保证自己的电脑上有安装git,不然下载n…...
上线流程及操作
上节回顾 1 搜索功能-前端:搜索框,搜索结果页面-后端:一种类型课程-APIResponse(actual_courseres.data.get(results),free_course[],light_course[])-搜索,如果数据量很大,直接使用mysql,效率非常低--》E…...
MobX入门指南:快速上手状态管理库
一、什么是MobX MobX 是一个状态管理库,它可以让你轻松地管理应用程序的状态,并且可以扩展和维护。它使用观察者模式来自动传播你的状态的变化到你的 React 组件。 二、安装及配置 安装 MobX 和 MobX-React:你可以使用 npm 或 yarn 安装这…...
技术洞察:Selenium WebDriver中Chrome, Edge, 和IE配置的关键区别
综述 webdriver.EdgeOptions(), webdriver.ChromeOptions(), 和 webdriver.IeOptions() 都是 Selenium WebDriver 的配置类,用于定制化启动各自浏览器的设置。它们分别对应 Microsoft Edge,Google Chrome,和 Internet Explorer 浏览器。 每…...
使用自定义OCR提升UIE-X检测效果:结合PaddleOCR和UIE模型进行文档信息提取
在实际应用中,识别文档中的特定信息对于许多任务至关重要,例如发票识别、表格信息提取等。然而,由于文档的多样性和复杂性,传统的光学字符识别(OCR)技术可能无法准确识别文档中的信息。为了解决这个问题&am…...
题目:写一个函数,求一个字符串的长度,在main函数中输入字符串,并输出其长度。
题目:写一个函数,求一个字符串的长度,在main函数中输入字符串,并输出其长度。 There is no nutrition in the blog content. After reading it, you will not only suffer from malnutrition, but also impotence. The blog con…...
.net反射(Reflection)
文章目录 一.概念:二.反射的作用:三.代码案例:四.运行结果: 一.概念: .NET 反射(Reflection)是指在运行时动态地检查、访问和修改程序集中的类型、成员和对象的能力。通过反射,你可…...
P1278 单词游戏 简单搜索+玄学优化
单词游戏 传送门 题目描述 Io 和 Ao 在玩一个单词游戏。 他们轮流说出一个仅包含元音字母的单词,并且后一个单词的第一个字母必须与前一个单词的最后一个字母一致。 游戏可以从任何一个单词开始。 任何单词禁止说两遍,游戏中只能使用给定词典中含有…...
软考 - 系统架构设计师 - 数据架构真题
问题 1: (相当于根据题目中提到的 4 点,说一下关系型数据库的缺点) (1).用户数量的剧增导致并发负载非常高,往往会达到每秒上万次读写请求。关系数据库应付每秒上万次的 SQL 查询还勉强可以,但是应付上万…...
Ubuntu22.04下opencv4.9.0环境的搭建
目录 1、更新系统包列表:2、安装依赖项:3、下载 OpenCV 源代码:4、编译和安装 OpenCV:5、配置环境变量:6、测试1、更新系统包列表: 在终端中执行以下命令,以确保系统包列表是最新的: sudo apt update2、安装依赖项: 安装构建 OpenCV 所需的依赖项: sudo apt inst…...
Flask如何在后端实时处理视频帧在前端展示
怎么样在前端->选择视频文件->点击上传视频后->后端实时分析上传的视频->在前端展示后端分析结果(视频,文本) ↓ 咱们先看整看整体代码,有个大概的印象。 Flask后端代码 cljc车流检测Demofrom pytz import timezon…...
04-15 周一 GitHub仓库CI服务器actions-runner和workflow yaml配置文档解析
04-15 周一 GitHub仓库CI服务器配置过程文档 时间版本修改人描述2024年4月15日10:35:52V0.1宋全恒新建文档2024年4月17日10:33:20v1.0宋全恒完成github actions CI的配置和工作流配置文件解读文档的撰写 简介 一些基础概念 前提知识 仓库介绍 地址镜像介绍https://github.…...
论文笔记:SmartPlay : A Benchmark for LLMs as Intelligent Agents
iclr 2024 reviewer评分 5688 引入了 SmartPlay,一种从 6 种不同游戏中提取的基准 衡量LLM作为智能体的能力 1 智能代理所需的能力 论文借鉴游戏设计的概念,确定了智能LLM代理的九项关键能力,并为每项能力确定了多个等级: 长文…...
搜维尔科技:【工业仿真】煤矿安全知识基础学习VR系统
产品概述 煤矿安全知识基础学习VR系统 系统内容: 煤矿安全知识基础学习VR系统内容包括:下井流程(正确乘坐罐笼、班前会、井下行走注意事项、工作服穿戴、入井检身及人员清点、下井前准备工作、提升运输安全);运煤流程…...
线程和进程的区别(面试)
线程和进程的区别 进程和线程的区别线程的优点 进程和线程的区别 1. 进程是系统进行资源分配和调度的一个独立单位,线程是程序执行的最小单位. 2. 进程有自己的内存地址空间,线程只独享指令流执行的必要资源,如寄存器和栈. 3. 由于同一进程的各线程共享内存和文件资源,可以不通…...
抓取电商产品数据的方法|电商平台商品详情数据|批量上架|商品搬家|电商封装API数据采集接口更高效安全的数据采集
大量级电商数据采集时使用电商API接口有以下优势: 1. 数据准确性:通过电商API接口获取数据,可以保证数据的准确性和实时性,避免了手动采集可能出现的错误和延迟。 2. 自动化采集:API接口可以实现自动化的数据获取和更…...
关联规则Apriori算法
1.前置知识 经典应用场景:购物车商品的关联规则。 符号表示: I代表项集,项是可能出现的值,例如购物车中能有尿布、啤酒、奶粉等,I{尿布、啤酒、奶粉},尿布是项 K代表I中包含的项的数目,上面的k3 事…...
书生·浦语大模型全链路开源体系-第4课
书生浦语大模型全链路开源体系-第4课 书生浦语大模型全链路开源体系-第4课相关资源XTuner 微调 LLMXTuner 微调小助手认知环境安装前期准备启动微调模型格式转换模型合并微调结果验证 将认知助手上传至OpenXLab将认知助手应用部署到OpenXLab使用XTuner微调多模态LLM前期准备启动…...
HTML优化SEO
在网站开发中,除了关注设计和用户体验,SEO(搜索引擎优化)也是提升网站流量和可见度的关键。合理的HTML结构和元素运用能够帮助搜索引擎更好地理解页面内容,从而提高搜索排名。以下是一些基于HTML的SEO优化技巧…...
三维GIS开发cesium智慧地铁教程(5)Cesium相机控制
一、环境搭建 <script src"../cesium1.99/Build/Cesium/Cesium.js"></script> <link rel"stylesheet" href"../cesium1.99/Build/Cesium/Widgets/widgets.css"> 关键配置点: 路径验证:确保相对路径.…...
多场景 OkHttpClient 管理器 - Android 网络通信解决方案
下面是一个完整的 Android 实现,展示如何创建和管理多个 OkHttpClient 实例,分别用于长连接、普通 HTTP 请求和文件下载场景。 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas…...
在 Nginx Stream 层“改写”MQTT ngx_stream_mqtt_filter_module
1、为什么要修改 CONNECT 报文? 多租户隔离:自动为接入设备追加租户前缀,后端按 ClientID 拆分队列。零代码鉴权:将入站用户名替换为 OAuth Access-Token,后端 Broker 统一校验。灰度发布:根据 IP/地理位写…...
渲染学进阶内容——模型
最近在写模组的时候发现渲染器里面离不开模型的定义,在渲染的第二篇文章中简单的讲解了一下关于模型部分的内容,其实不管是方块还是方块实体,都离不开模型的内容 🧱 一、CubeListBuilder 功能解析 CubeListBuilder 是 Minecraft Java 版模型系统的核心构建器,用于动态创…...
在web-view 加载的本地及远程HTML中调用uniapp的API及网页和vue页面是如何通讯的?
uni-app 中 Web-view 与 Vue 页面的通讯机制详解 一、Web-view 简介 Web-view 是 uni-app 提供的一个重要组件,用于在原生应用中加载 HTML 页面: 支持加载本地 HTML 文件支持加载远程 HTML 页面实现 Web 与原生的双向通讯可用于嵌入第三方网页或 H5 应…...
技术栈RabbitMq的介绍和使用
目录 1. 什么是消息队列?2. 消息队列的优点3. RabbitMQ 消息队列概述4. RabbitMQ 安装5. Exchange 四种类型5.1 direct 精准匹配5.2 fanout 广播5.3 topic 正则匹配 6. RabbitMQ 队列模式6.1 简单队列模式6.2 工作队列模式6.3 发布/订阅模式6.4 路由模式6.5 主题模式…...
AI+无人机如何守护濒危物种?YOLOv8实现95%精准识别
【导读】 野生动物监测在理解和保护生态系统中发挥着至关重要的作用。然而,传统的野生动物观察方法往往耗时耗力、成本高昂且范围有限。无人机的出现为野生动物监测提供了有前景的替代方案,能够实现大范围覆盖并远程采集数据。尽管具备这些优势…...
C++课设:简易日历程序(支持传统节假日 + 二十四节气 + 个人纪念日管理)
名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 专栏介绍:《编程项目实战》 目录 一、为什么要开发一个日历程序?1. 深入理解时间算法2. 练习面向对象设计3. 学习数据结构应用二、核心算法深度解析…...
怎么让Comfyui导出的图像不包含工作流信息,
为了数据安全,让Comfyui导出的图像不包含工作流信息,导出的图像就不会拖到comfyui中加载出来工作流。 ComfyUI的目录下node.py 直接移除 pnginfo(推荐) 在 save_images 方法中,删除或注释掉所有与 metadata …...
【堆垛策略】设计方法
堆垛策略的设计是积木堆叠系统的核心,直接影响堆叠的稳定性、效率和容错能力。以下是分层次的堆垛策略设计方法,涵盖基础规则、优化算法和容错机制: 1. 基础堆垛规则 (1) 物理稳定性优先 重心原则: 大尺寸/重量积木在下…...












