字符串函数(二)—— 长度受限制的字符串函数

图片来源于网络

✨博客主页:小钱编程成长记
🎈博客专栏:进阶C语言
🎈相关博文:字符串函数(一)

字符串函数(二)—— 长度受限制的字符串函数

  • 3.长度受限制的字符串函数
    • 3.1 strncpy(指定操作长度的拷贝字符串)
    • 3.2 strncat(指定操作长度的字符串追加)
    • 3.3 strncmp(指定操作长度的字符串比较)
    • 3.4 strstr(查找子字符串 / 在字符串中找字符串)
    • 3.5 strtok(字符串切割 / 分隔)
    • 3.6 strerror(翻译错误码)
  • 总结

3.长度受限制的字符串函数

要指定操作长度,思考的更多,相对更安全

3.1 strncpy(指定操作长度的拷贝字符串)

具体介绍链接

char * strncpy ( char * destination, const char * source, size_t num );
介绍:

将源字符串的第一个字符拷贝到目标字符串。如果在拷贝完 num 个字符之前找到源 C
字符串的末尾(由’\0’表示),则目标将填充0,直到总共写入 num 个字符为止。

注:
  • 从源字符串拷贝num个字符到目标空间。

  • 如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个。在这里插入图片描述

  • 源字符串(字符数组) 不需要一定以’\0’结束。(因为操作数不同,新的目标字符串末尾会自动补充’\0’)。

  • 目标空间必须足够大,以确保能存放源字符串。(否则会报错)

  • 目标空间必须可变。(因为要把另一个字符串拷贝到这里)

  • 返回值是目标空间的起始地址,然后通过%s来打印,%s是从给的地址开始 *解引用打印,遇到’\0’结束

  • 学会模拟实现。

模拟实现strncpy :

//模拟strncpy
#include <stdio.h>
#include <assert.h>char* my_strncpy(char* dest, const char* src, size_t num)
{assert(dest && src);char* start = dest;int i = 0;for (i = 0; i < num; i++){if (*src != '\0'){*dest++ = *src++;}else{*dest++ = '\0';//写0 或 '\0'都一样,因为在内存中'\0'(ASCII码)就是0}}//num <= 源字符串长度(不包含'\0') 时,新目标字符串中没有'\0',要再加个'\0'if (*(dest-1) != '\0')//-1是因为循环结束时dest指向的是第num+1个数{*dest = '\0';//在num个数后面加个结束标志'\0'}return start;
}int main()
{char arr1[20] = "xxxxxxxxxxxxxxx";char arr2[] = "abcdef";my_strncpy(arr1, arr2, 10);printf("%s\n", arr1);return 0;
}

在这里插入图片描述

3.2 strncat(指定操作长度的字符串追加)

具体介绍链接

char * strncat ( char * destination, const char * source, size_t num );
注 :
  • 从目标空间的第一个 ‘\0’ 开始('\0’被覆盖),追加规定的字符个数,追加完后还会在后面补充一个 ‘\0’ ,这样才构成了一个完整的字符串。在这里插入图片描述

  • 如果指定追加的字符个数 > 源字符串的字符个数,则在实际追加时只追加现有源字符串 ‘\0’ 之前的全部内容。
    在这里插入图片描述

  • 源字符串(字符数组) 不需要一定以’\0’结束。(因为操作数不同,新的目标字符串末尾会自动补充’\0’)。
    在这里插入图片描述

  • 目标空间必须要有’\0’,保证能找到目标空间的末尾,进行追加。(编译器认为从左到右第一个’\0’是字符串的末尾)

  • 目标空间必须有足够的大,能容纳下源字符串的内容。

  • 目标空间必须可修改。

  • strncat返回的是目标空间的起始地址。

  • 学会模拟实现。

模拟实现strncpy :

//模拟实现strncat
#include <stdio.h>
#include <assert.h>char* my_strncat(char* dest, const char* src, size_t num)
{assert(dest && src);//断言char* start = dest;//找目标函数的末尾'\0'while (*dest){dest++;}//数据追加int i = 0;for (i = 0; i < num; i++){if (*src != '\0'){*dest++ = *src++;}else//*src == '\0'时跳出循环,在新目标字符串末尾补充'\0'{break;}}*dest = '\0';//在末尾补充'\0'return start;
}int main()
{char arr1[20] = "abc\0xxxxxxxxxxxx";char arr2[] = "def";my_strncat(arr1, arr2, 1);printf("%s\n", arr1);return 0;
}

3.3 strncmp(指定操作长度的字符串比较)

具体介绍链接

int strncmp ( const char * str1, const char * str2, size_t num );
介绍:
  1. 此函数开始比较每个字符串的第一个字符,如果它们相等,则继续向下比较,直到字符不同达到终止空字符(‘\0’)拷贝完num个字符 停止。
  2. 比较的不是长度,而是对应位置上字符的大小ASCII码,因为字符在内存中是以ASCII码的形式存储的)
C语言标准规定:
返回值(整型)解释
大于 0第一个字符串大于第二个字符串
0第一个字符串等于第二个字符串
小于 0第一个字符串小于第二个字符串
不同的编译器具体返回的值不同

3.4 strstr(查找子字符串 / 在字符串中找字符串)

具体介绍链接

const char * strstr(const char *str1, const char *str2);
介绍:

在str1中查找str2,strstr会返回str1中str2第一次出现的位置的第一个字符的地址;如果str1中没str2,则返回NULL(空指针)。

在这里插入图片描述

若在arr1上用str1遍历,如图第二次遍历时没找到子字符串,那就需要从第3个字符处再遍历,但是唯一的一个指针被我们用来遍历了,我们现在连首字符都不知道在哪了,所以我们要提前保留初始指针,创建一个专门用来对比遍历的指针,arr2也是一样。在arr1中也可以再创建个指针用来指向每次遍历的第一个字符,将初始指针保存起来,在遇到复杂问题时会更加方便,更加得心应手。

因此:
当出现需要多个指针、指针需要移动等比较复杂的情况时,原始的指针最好不要移动或改变。

第一次遍历时,a与b不相同,第二次遍历从a后面一个字符再开始遍历。
在这里插入图片描述
b和c不同,
开始第三次遍历:
在这里插入图片描述
当s2指向’\0’时,说明在arr1中能找到arr2,查找结束。

模拟实现strstr
//模拟实现strstr
#include <stdio.h>
#include <assert.h>const char* my_strstr(const char* str1, const char* str2)
{assert(str1 && str2);//断言,防止为空指针//写不写都行,因为这种情况,后面的代码已经考虑到了if (*str2 == '\0')//如果要找的字符串为空,则说明在arr1中直接找完了 / (没有要找的字符串,指针没有移动),直接返回arr1的首字符地址{return str1;}//cp,s1,s2指向的数据都不需要改动,可以用const保护起来const char* cp = str1;//cp用来记录开始遍历(匹配)的位置const char* s1 = cp;//s1用来遍历str1指向的字符串const char* s2 = str2;//s2用来遍历str2指向的字符串while (*cp)//如果arr1中第一个字符就是'0',说明是个空字符串,一定找不到arr2,直接返回NULL{s1 = cp;s2 = str2;while (*s1 && *s2 && *s1 == *s2)//三个条件要同时为真。//若*s1为假--*s1=='\0' 说明指针已经指向了arr1的末尾,此时还没找到arr2,说明arr1中没有arr2,跳出循环,返回NULL//若*s2为假--*s2=='\0' 说明arr1中找到了arr2,跳出循环,函数返回str1中str2第一次出现的位置的第一个字符地址//若*s1 == *s2说明两个字符相等,继续遍历{s1++;s2++;}if (*s2 == '\0')//说明arr1中找到了arr2,函数返回str1中str2第一次出现的位置的第一个字符地址{return cp;}else if (*s1 == '\0')//说明指针已经指向了arr1的末尾,此时还没找到arr2,说明arr1中没有arr2,返回NULL{return NULL;}cp++;}return NULL;//*cp为假--*cp=='\0'  说明指针已经指向了arr1的末尾,此时还没找到arr2,说明arr1中没有arr2,返回NULL
}int main()
{char arr1[] = "abbcdef";char arr2[] = "bbc";//函数返回的指针是受保护的const char*类型的,赋给不受保护的char*类型指针,属于类型放大了(容易丢失精度不太安全),最好赋给const char*类型指针char* ret = my_strstr(arr1, arr2);printf("%s\n", ret);//%s是从给的地址开始 *解引用打印,遇到'\0'结束return 0;
}

这种模拟实现是一种暴力求解,算法不够高效,后期会用KMP算法实现,更高效。

3.5 strtok(字符串切割 / 分隔)

具体介绍链接

char * strtok(char *str, const char *sep);
介绍:

strtok是个有点奇怪的函数,它和之前见过的函数都不一样。 是用来拆分字符串的,比如:
在这里插入图片描述

  • 参数sep是个字符串,定义了用作分隔符的字符集合。分隔符在字符串中的顺序无所谓。

  • 第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或多个分隔符分割的标记。

  • strtok找到str中的下一个标记,并将其用 \0 结尾(替换掉原来的分隔符),返回一个指向这个标记首字符的指针。若下一个标记的结尾是\0,则也返回这个标记的首字符地址。
    ( 注:strtok函数会改变被操作的字符串,所以使用strtok函数切分的字符串一般都是临时拷贝的并且可修改的。)
    在这里插入图片描述

  • 若strtok函数的第一个参数不为 NULL ,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置 或者说是保存这个标记末尾 \0 的位置。

  • 若strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标记。(有记忆功能(static))

  • 如果字符串中不存在更多的标记或查找到了 \0,则返回 NULL 指针。

//strtok
#include <stdio.h>
#include <string.h>int main()
{char arr[] = "0111222333@qq.com";char buf[200] = { 0 };strcpy(buf, arr);//buf的内容是临时拷贝的并且是可修改的char* p = "@.";//和char p[] = "@." 几乎相同,p都是字符串的首字符地址,内存中也都存储了字符串char* s = strtok(buf, p);//strtok函数的第一个参数不为NULL,找buf中的第一个标记,并且找到后用\0替换标记符来结尾printf("%s\n", s);//找到了一个记录,返回这个记录的首字符地址s = strtok(NULL, p);//strtok函数的第一个参数为NULL,从同一个字符串中被保存的上一个标记的末尾\0开始,查找下一个标记printf("%s\n", s);s = strtok(NULL, p);printf("%s\n", p);return 0;
}

在未来真正使用这个函数时,并不是这样使用的,应该是这样:

//strtok函数的正确使用方式1:
#include <stdio.h>
#include <string.h>int main()
{char arr[] = "0111222333@qq.com";char buf[200] = { 0 };strcpy(buf, arr);char p[] = "@.";// == char *p = "@.";//分隔符在字符串中的顺序无所谓char* s = 0;//strtok的返回值//当字符串中不存在更多标记时,返回NULL空指针,循环的条件判断部分为假,跳出循环。for (s = strtok(buf, p); s != '\0'; s = strtok(NULL, p)){printf("%s\n", s);}return 0;
}//使用方式2:
#include <stdio.h>
#include <string.h>int main()
{char arr[] = "0111222333@!qq.com";char buf[200] = { 0 };strcpy(buf, arr);char p[] = "@.!";// == char *p = "@.!";char* s = 0;//strtok的返回值s = strtok(buf, p);//strtok函数的第一个参数不为NULL,找buf中的第一个标记,找到后用\0替换标记符来结尾while (s != NULL){printf("%s\n", s);s = strtok(NULL, p);//strtok函数的第一个参数为NULL,从同一个字符串中被保存的上一个标记的末尾\0开始,查找下一个标记}return 0;
}

若出现两个分隔符连在一起了,则第二个分隔符直接被跳过,如图:
在这里插入图片描述

3.6 strerror(翻译错误码)

具体介绍链接

char * strerror ( int errnum );
介绍:

将错误码翻译成错误信息,返回错误信息的字符串的起始地址。
(只能将C语言标准库中的错误码翻译成错误信息)

注:

C语言中使用库函数的时候,如果发生错误,就会将错误码放在errno的变量中;errno是个全局的变量,可直接使用,需要头文件errno.h

》打开文件的例子:
fopen以读的形式打开文件(头文件stdio.h),
如果文件存在,则打开成功;
如果文件不存在,则打开失败;

//strerror
#include <stdio.h>//main,printf,fopen,perror的头文件
#include <string.h>//strerror的头文件
#include <errno.h>//errno的头文件int main()
{FILE* pfile = fopen("add.txt", "r");//若打开成功,则返回FILE*类型的指针if (pfile == NULL)//若打开失败,则返回NULL空指针{printf("打开文件失败,原因是:%s\n", strerror(errno));}else{printf("打开文件成功\n");}return 0;
}

在这里插入图片描述

若在此源文件所在的文件夹中有add.txt这个文件,则打开文件成功,否则失败。
在这里插入图片描述

补充小知识:

perror可直接打印错误码所对应的错误信息。
perror == printf + strerror
在这里插入图片描述

总结

我们一起学习了长度受限制的字符串函数。
感谢大家的阅读,大家一起进步!

点赞收藏加关注,C语言学习不迷路!
图片来源于网络

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

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

相关文章

Redis配置和优化

Redis配置和优化 一 、Redis介绍二、关系数据库和非关系数据库2.1、关系型数据库2.2、 非关系型数据库2.3、 非关系型数据库的产生背景2.4、 关系型数据库和非关系型数据库区别2.5、 总结 三、缓存概念3.1、系统缓存3.2、 缓存保存位置及分层结构3.2.1、DNS缓存3.2.2、 应用层缓…

MySql进阶篇---006:存储引擎,索引,SQL优化,视图、存储过程、变量、流程控制、游标、存储函数、触发器

1. 存储引擎 1.1 MySQL体系结构 1).连接层 最上层是一些客户端和链接服务&#xff0c;包含本地sock 通信和大多数基于客户端/服务端工具实现的类似于TCP/IP的通信。主要完成一些类似于连接处理、授权认证、及相关的安全方案。在该层上引入了线程池的概念&#xff0c;为通过认证…

Java大数 -- BigInteger类

在java语言中&#xff0c;每一种整数类型都有自己的上限和下限&#xff0c;如果要想对非常大的整数做运算&#xff0c;就需要使用BigInteger类。特别在做算法题传入一个数值型的字符串时。 1.包 import java.math.*; 2.构造方法 public BigInteger(String str){...} BigInte…

10.1select并发服务器以及客户端

服务器&#xff1a; #include<myhead.h>//do-while只是为了不让花括号单独存在&#xff0c;并不循环 #define ERR_MSG(msg) do{\fprintf(stderr,"%d:",__LINE__);\perror(msg);\ }while(0);#define PORT 8888//端口号1024-49151 #define IP "192.168.2.5…

三等分功分器[波导]设计详细教程

想必大家通过阅读相关文献可以发现三等分实现可以有很多不同的方法&#xff0c;这里采用的是先不等分再等分的方式&#xff0c;仅供参考。 主要指标 中心频率为280GHz&#xff0c;采用WR-3频段的标准波导&#xff0c;将2:1不等功率分配耦合器与3dB等功率分配耦合器级联&#…

动态规划:两个数组的dp问题(C++)

动态规划&#xff1a;两个数组的dp问题 前言两个数组的dp问题1.最长公共子序列&#xff08;中等&#xff09;2.不同的子序列&#xff08;困难&#xff09;3.通配符匹配&#xff08;困难&#xff09;4.正则表达式&#xff08;困难&#xff09;5.交错字符串&#xff08;中等&…

【NLP的python库(03/4) 】: 全面概述

一、说明 Python 对自然语言处理库有丰富的支持。从文本处理、标记化文本并确定其引理开始&#xff0c;到句法分析、解析文本并分配句法角色&#xff0c;再到语义处理&#xff0c;例如识别命名实体、情感分析和文档分类&#xff0c;一切都由至少一个库提供。那么&#xff0c;你…

加入PreAuthorize注解鉴权之后NullPointerException报错

记录一次很坑的bug&#xff0c;加入PreAuthorize注解鉴权之后NullPointerException报错&#xff0c;按理来说没有权限应该403报错&#xff0c;但是这个是500报错&#xff0c;原因是因为controller层的service注入失败&#xff0c;然而我去掉注解后service注入成功&#xff0c;并…

初级篇—第三章多表查询

文章目录 为什么需要多表查询一个案例引发的多表连接初代查询笛卡尔积&#xff08;或交叉连接&#xff09;的理解 多表查询分类等值连接 vs 非等值连接自连接 vs 非自连接内连接VS外连接 SQL99语法实现多表查询内连接的实现外连接的实现左外连接右外连接满外连接 UNION的使用7种…

Mysql高手养成——第一章:索引知识,浅尝性能分析

&#x1f4e3; &#x1f4e3; &#x1f4e3; &#x1f4e2;&#x1f4e2;&#x1f4e2; 我是小冷 侧重后端的全栈工程师&#xff0c;有关技术问题需要讨论交流的直接私信即可 ⏩当前专栏&#xff1a;mysql高手养成系列- 第一章 索引与浅尝性能分析 ✏️高质量技术专栏专栏链接:…

解决前端二进制流下载的文件(例如:excel)打不开的问题

1. 现在后端请求数据后&#xff0c;返回了一个二进制的数据&#xff0c;我们要把它下载下来。 这是响应的数据&#xff1a; 2. 这是调用接口的地方&#xff1a; uploadOk(){if(this.files.length 0){return this.$Message.warning("请选择上传文件&#xff01;&#xff…

vite跨域proxy设置与开发、生产环境的接口配置,接口在生产环境下,还能使用proxy代理地址吗

文章目录 vite的proxy开发环境设置如果后端没有提供可以替换的/mis等可替换的后缀的处理办法接口如何区分.env.development开发和.env.production生产环境接口在生产环境下&#xff0c;还能使用proxy代理地址吗&#xff1f; vite的proxy开发环境设置 环境&#xff1a; vite 4…

Lua学习笔记:require非.lua拓展名的文件

前言 本篇在讲什么 Lua的require相关的内容 本篇需要什么 对Lua语法有简单认知 对C语法有简单认知 依赖Visual Studio工具 本篇的特色 具有全流程的图文教学 重实践&#xff0c;轻理论&#xff0c;快速上手 提供全流程的源码内容 ★提高阅读体验★ &#x1f449; ♠…

ChatGPT的截图识别功能测评:开启图像中的文字与信息的新纪元

文章目录 根据截图&#xff0c;识别菜品根据截图&#xff0c;识别数学公式根据截图生成前端UI代码可视化图像复现案例一案例二 更多可以使用的方向 制作人&#xff1a;川川 辛苦测评&#xff0c;如果对你有帮助支持一下书籍&#xff1a;https://item.jd.com/14049708.html 根据…

微信小程序,动态设置三级联动, 省市区街道

1.第一步 传parentId0 查询省份 2.第二步 选择省份,传pathId选择省份的pathId, 不传parentId,会查询出 市/县数据 3.第三步 根据选择县的parentId 查询街道数据,传parentId选择的县id 4.选择结果回显 显示所选择的 path 以/分割 取最后一级<van-dropdown-menu…

wustctf2020_name_your_cat

wustctf2020_name_your_cat Arch: i386-32-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x8048000)32位&#xff0c;开了NX和canary int shell() {return system("/bin/sh"); }有个后门函数 unsigned int…

【量化】量化原理浅析

前言 模型在端侧运行时&#xff0c;会追求模型保持原有精度的同时&#xff0c;让模型的运行速度更快。基本方向为模型压缩和加速&#xff0c;着力于减少网络参数量、降低计算复杂度。可通过以下方式实现&#xff1a; 针对网络结构本身进行改进&#xff0c;常用的3x3的卷积的叠加…

重置Jetson设备的Ubuntu密码:通过挂载根目录到另一个Linux系统

在本文中&#xff0c;我们将介绍如何在忘记Ubuntu 20.04密码的情况下重置密码。我们将通过将Ubuntu的根目录挂载到另一个Linux系统来实现这一目的。我们还将介绍chroot命令的功能。 1. 背景 最近&#xff0c;我们研发团队遇到了一个棘手的问题。一台用于研发&#xff0c;多人使…

大数据Doris(三):Doris编译部署篇

文章目录 Doris编译部署篇 一、Doris编译

云计算安全:保护你的数据免受黑客侵害

文章目录 云计算的崛起云计算安全的挑战1. 数据隐私2. 身份认证和访问控制3. 网络安全4. 云供应商安全 云计算安全的最佳实践1. 数据加密2. 强身份认证3. 访问控制4. 安全审计5. 更新和漏洞管理6. 培训和教育 云计算安全的未来1. 量子安全性2. 人工智能和机器学习3. 边缘计算安…