【C进阶】字符串函数

 C语言中对字符和字符串的处理很频繁,但是C语言本身是没有字符串类型的,字符串通常放在常量字符串中或者字符数组

字符串常量适用于那些对它不做修改的字符串函数

本章重点介绍处理字符串函数的库函数的使用和注意事项


一、字符串函数

这些函数都要引头文件:#include<string.h>

1.1 strlen

size_t  strlen(const char *str)

(1)库函数,是用来求字符串长度的,本质上统计的是字符串中\0之前的字符的个数(不包括’\0‘)

(2)参数指向的字符串(传地址)必须要以 '\0' 结束

(3)注意函数的返回值为size_t,是无符号的(易错)


下面看看这个代码:思考一下结果是什么?

#include<stdio.h>
#include<string.h>
int main()
{const char* str1 = "abcdef";const char* str2 = "bbb";if (strlen(str2) - strlen(str1) > 0){printf("str2>str1\n");}else{printf("srt1>str2\n");}return 0;
}

【答案】:srt2>str1

【解释】:两个无符号数相减得到的还是无符号数,那么一个小的数减去一个大的数还是得不到负数,所以就会打印srt2>str1

【改进】:将size_t类型强转为int类型

if ((int)strlen(str2) - (int)strlen(str1) > 0)


strlen函数的三种模拟实现:

(1)计数器:

size_t my_strlen(const char* str)
{int count = 0;while (*str++){count++;}return count;
}

(2)递归:

size_t my_strlen(const char* str)
{if (*str)return my_strlen(str+1) + 1;elsereturn 0;
}

(3)指针-指针

size_t my_strlen(const char* str)
{char* p = str;while (*p){p++;}return p - str;
}

1.2 strcpy

char* strcpy(char * destination, const char * source )

把 source所指向的字符串复制到 destination,而且会覆盖destination中的字符(字符串拷贝)

(1) 源字符串必须以 '\0' 结束

(2)会将源字符串中的 '\0' 拷贝到目标空间(不然找不到‘\0'没法结束,就可能会导致数组越界)

(3)目标空间必须足够大,以确保能存放源字符串

(4)目标空间必须可变

(5)返回值是目标空间的地址(参数前面是目标字符串的地址,后面是源头字符串的地址)


验证strcpy会将源字符串中的'\0'拷贝到目标空间: 

#include<stdio.h>
#include<string.h>
int main()
{char arr1[20] = "WORLDGOOD";char arr2[] = "HELLO";strcpy(arr1, arr2);printf("%s\n", arr1);return 0;
}

未strcpy前的arr1: 

strcpy后的arr1: 可以看到下标为5的元素就是'\0'


 当目标空间为常量字符串时,不满足条件(4),那么就会出现错误:

#include<stdio.h>
#include<string.h>
int main()
{char* p = "abcdefghi";//常量字符串char arr2[] = "HELLO";strcpy(p, arr2);//errprintf("%s\n", p);return 0;
}

 strcpy函数的模拟实现: 
#include<assert.h>
char* my_strcpy(char* dest, const char* src)
{char* ret = dest;assert(dest && src);//防止dest和src是空指针while (*dest++ = *src++){;}return ret;
}

1.3 strcat

char * strcat(char * destination ,const char * source)

把 source所指向的字符串追加到 destination所指向的字符串的结尾(字符串追加)

内部逻辑:

<1>找到destination的末尾'\0'

<2>再把source的内容追加到destination的后面,会把前面字符串的’\0'覆盖

(1)源字符串必须以'\0'结束(’\0'也会追加过去)

(2)目标空间必须足够大,而且必须可以修改

(3)目标字符串中也得有’\0',保证可以找到目标空间的末尾进行追加

(4)字符串不能自己给自己追加:这样会进入死循环,前面的'\0'被覆盖了,没有’\0'无法结束


 strcpy函数的模拟实现: 

<1>找到目标空间的末尾

<2>数据的追加(跟strcpy函数一样)

char* my_strcat(char* dest,const char* src)
{char* ret = dest;assert(dest && src);//1.找到目标空间的末尾while (*dest != '\0'){dest++;}//2.数据的追加(跟strcpy函数一样)while (*dest++ = *src++){;}return ret;
}

 1.4 strcmp

int strcmp (const char * str1,const char *str2)

(string compare)字符串比较函数

(1)不是比较长度,而是比较对应位置字符的大小(也就是ACSII值)

eg: str1=”abcdef“  str2=“abq" 

在这个两个字符串前面两个字符都一样,但是到第三个字符位置上,str1是c,str2是q,q的ACSII值大于c,那么最后str1<str2

(2)标准规定:

str1>str2,则返回大于0的数

str1<str2,则返回小于0的数

str1和str2相等,则返回等于0的数

 在VS中返回值就是-1,0,1,但是其他编译器就不一定


  strcmp函数的模拟实现:

(1)初阶版:

#include<assert.h>
int my_strcmp(const char* str1, const char* str2)
{assert(str1 && str2);while (*str1==*str2){if (*str1 == '\0')return 0;str1++;str2++;}if (*str1 > *str2){return 1;}else{return -1;}}

(2) 进阶版:这样简化了很多,而且返回值不只是1或者-1

#include<assert.h>
int my_strcmp(const char* str1, const char* str2)
{assert(str1 && str2);while (*str1==*str2){if (*str1 == '\0')return 0;str1++;str2++;}return *str1 - *str2;
}

 上面介绍的都是长度不受限制的字符串函数(strcpy,strcat,strcmp)

下面再来学习一下长度受限制的字符串函数:(strncpy,strncat,strncmp)

2.1 strncpy

char * strncpy(char  * destination,const char *source,size_t num)

拷贝num个字符从源字符串到目标空间

(1)如果源字符串的长度小于num,则拷贝完源字符串之后,在目标空间的后面追加’\0',直到num个

#include<stdio.h>
#include<string.h>
int main()
{char arr1[20] = "XXXXXXXXXXXXX";char arr2[] = "abc";strncpy(arr1, arr2, 6);printf("%s\n", arr1);return 0;
}

我们可以看到arr2只有四个元素,但是要传6个字符,那么就自动在后面补上’\0',直到补到num个为止(这里printf打印arr1时,后面的XXXX是不会打印的,因为字符串遇到‘\0'就结束了,就不会再向后走了)


2.2 strncat 

char * strncat(char * destination,const char *source,size_t num)

 如果num大于源字符串的长度,只会追加源字符串的长度

#include<stdio.h>
#include<string.h>
int main()
{char arr1[20] = "abc\0xxxxxxxxxxxxx";  //一定要有\0,不然找不到字符串的末尾char arr2[] = "defghi";strncat(arr1, arr2, 10);printf("%s\n", arr1);return 0;
}

 2.3 strncmp

int strncmp(const char * str1, const char *str2)

比较到出现另一个字符不一样或者一个字符串结束或者num个字符全部比较完、

#include<stdio.h>
#include<string.h>
int main()
{char arr1[] = "abczef";char arr2[] = "abcqw";int ret = strncmp(arr1, arr2, 4);printf("%d\n", ret);return 0;
}

3.1 strstr

char * strstr (const char * str1,const char *str2)

 string string 在字符串中找字符串

(1)strstr会返回str1中str2第一次出现的位置(多次出现只会返回第一次出现的地址)

         如果str1中没有str2,就返回NULL

eg:代码结果:defghi

#include<stdio.h>
#include<string.h>
int main()
{char arr1[] = "abcdefghi";char arr2[] = "def";char* ret = strstr(arr1, arr2);if (ret == NULL){printf("找不到\n");}else{printf("%s\n", ret);}return 0;
}
   strstr函数的模拟实现:

(1)得有一个指针(cp)记录从哪开始str1和str2相匹配了

(2)对于str1="abbbcdef\0"  str2="bbc\0”就没有那么简单了(当str1指向第三个b时,发现str2对应的不是b而是c,那么有得重新开始,所以还得有个指针指向str2的头部得以返回)

我们就考虑再加两个指针s1和s2分别去遍历str1和str2,str1和str2就指向这两个字符串的头部

#include<stdio.h>
#include<assert.h>
const char *my_strstr(const char* str1, const char* str2)
{const char* cp=str1;//记录开始匹配的位置const char* s1 ;const char* s2 ;//分别去遍历字符串assert(str1 && str2);if (*str2 == '\0')return str1;//空字符串是所有字符串的子字符串while (*cp)//如果str1是空字符就是直接跳出循环{s1 = cp;//重新开始s1就得从cp开始s2 = str2;//s2从str2开始while (*s1&&*s2&& *s1 == *s2)//s1和s2不相等或者都为0就停下来{s1++;s2++;}if (*s2 == '\0')return cp;cp++;//s1和s2不相等就cp向后走一个位置}return NULL;//找不到返回空指针
}

 这个strstr的模拟实现是一种暴力求解的方式,算法不够高效,有兴趣可以去研究KMP算法


3.2 strtok

char * strtok (char *str ,const char *sep)

这个函数是用来切割字符串

eg:shenyu@yeah.net(yeah是域名,.net是后缀)//@和.就切割了字符串

192.168.101.23  //IP地址,点分十进制

(IP地址本来是一个无符号整数,这个整数不方便记忆,所以将这个整数转化为点分十进制的表示方式)

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

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

(3)strtok函数找到str中的下一个标记,并将其用 \0 结尾(把这个标记改为‘\0'),返回一个指向这个标记的指针(有记忆功能)(注: strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可修改)

(4)strtok函数的第一个参数不为 NULL ,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置(第一次传参:s = strtok(arr, p))

(5)strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标记(后面传参:s = strtok(NULL, p))

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

理解怎么使用:

#include<stdio.h>
#include<string.h>
int main()
{char arr[] = "shenyu@yeah.net";char buf[200] = { 0 };strcpy(buf, arr);//拷贝一份char* p = "@.";char* s = strtok(buf, p);printf("%s\n", s);s = strtok(NULL, p);printf("%s\n", s);s = strtok(NULL, p);printf("%s\n", s);return 0;
}

代码结果: 

 

奇葩的函数:第一次调用和后面的调用传递的参数不一样

真正使用strtok函数的代码:这个代码的结果和上面一样,for直接打印所有分割的字符

#include<stdio.h>
#include<string.h>
int main()
{char arr[] = "shenyu@yeah.net";char buf[200] = { 0 };strcpy(buf, arr);//拷贝一份char* p = "@.";char* s = NULL;for (s = strtok(arr, p); s != NULL; s = strtok(NULL, p)){printf("%s\n", s);}return 0;
}

3.3 strerror

char * strerror (int errnum)

 错误码翻译错误信息,返回错误信息的字符串的起始地址

当使用库函数或者进行整个的软件设计的过程中,可能会有错误码 eg:404

C语言中使用库函数的时候,如果发生错误,就会将错误码放在errno的变量中

errno是一个全局的变量,可以直接使用的 eg:0,1,2,3,4,5...

下面我们来看看错误码对应的错误信息:

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

结果如下: 

再来个打开文件的例子:

fopen以读的形式打开文件
如果文件存在,打开成功
如果文件不存在,打开失败 

用errno变量要引头文件:#include<errno.h> 

#include<stdio.h>
#include<errno.h>
#include<string.h>
int main()
{FILE *pf=fopen("add.txt", "r");if (pf == NULL){printf("打开文件失败,原因是:%s\n", strerror(errno));return 1;}else{printf("打开文件成功\n");}return 0;
}

拓展:perror函数:直接打印错误所对应的错误信息

相当于printf+strerror(打印规则:自定义信息+xxxxx)

#include<stdio.h>
int main()
{FILE *pf=fopen("add.txt", "r");if (pf == NULL){perror("打开文件失败");}else{printf("打开文件成功\n");}return 0;
}

 


二、字符分类函数: 

这些函数都需要引头文件:#include <ctype.h>

函数如果他的参数符合以下条件就返回真
iscntrl任何控制字符
isspace

空白字符:空格’ ‘,换页’\f',换行‘\n',回车’\r',制表符‘\t'或者垂直制表符’\v'

isdigit十进制数字0~9
isxdigit十六进制数字,包括所以十进制数字,小写字母a~f,大写字母A~F
islower小写字母a~z
isupper大写字母A~Z
isalpha字母a~z或A~Z
isalnum字母或者数字,a~z,A~Z,0~9

ispunct

标点符号,任何不属于数字或者字母的图形字符(可打印)
isgraph任何图形字符
isprint任何可打印字符,包括图形字符和空白字符

eg1:判断一个字符是否是小写字母 (    int islower (int c)  ) 

#include<stdio.h>
int main()
{char ch = 'w';if (islower(ch)){printf("小写\n");}else{printf("非小写\n");}return 0;
}

 eg2:判断一个字符是不是十六进制数字

#include<stdio.h>
int main()
{int ret = isxdigit('q');printf("%d\n", ret);return 0;
}

 三、字符转换函数

这些函数都需要引头文件:#include <ctype.h>

int tolower(int c);

int  toupper(int c);

 eg1:大写转小写,小写转大写

#include<stdio.h>
int main()
{int ret = tolower('a');printf("%c\n", ret);//Aret = tolower(ret);printf("%c\n", ret);//areturn 0;
}

eg2:字符串大写字母全部转为小写

#include<stdio.h>
int main()
{char arr[] = "Test String.\n";char* p = arr;while (*p){if (isupper(*p)){*p = tolower(*p);}p++;}printf("%s", arr);return 0;
}

本次内容就到此啦,欢迎评论区或者私信交流,觉得笔者写的还可以,或者自己有些许收获的,麻烦铁汁们动动小手,给俺来个一键三连,万分感谢 ! 

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

服务器数据恢复-VMWARE ESX SERVER虚拟机数据恢复案例

服务器数据恢复环境&#xff1a; 几台VMware ESX SERVER共享一台某品牌存储&#xff0c;共有几十组虚拟机。 服务器故障&#xff1a; 虚拟机在工作过程中突然被发现不可用&#xff0c;管理员将设备进行了重启&#xff0c;重启后虚拟机依然不可用&#xff0c;虚拟磁盘丢失&#…

学习Origin

最近&#xff0c;在学习Origin软件&#xff0c;网上资源还是很多的。我简单地记录了Origin的一些知识点&#xff0c;来督促自己的学习。 了解一下Origin的作用。 Origin入门教程&#xff08;一&#xff09;&#xff1a;一文学会Origin (sousepad.com) 该文讲述了Origin的一些基…

【SpringMVC篇】详解SpringMVC入门案例

&#x1f38a;专栏【SpringMVC】 &#x1f354;喜欢的诗句&#xff1a;天行健&#xff0c;君子以自强不息。 &#x1f386;音乐分享【如愿】 &#x1f384;欢迎并且感谢大家指出小吉的问题&#x1f970; 文章目录 &#x1f38d;SpringMVC简介⭐优点 &#x1f33a;SpringMVC入门…

vscode的窗口下拉显示行数不够

这是为了减少程序的空间占用而存在的一个设置。设置一下即可。 设置方法 在左上角文件&#xff0c;个人设置&#xff0c;设置中&#xff0c;&#xff08;或者用Ctrl&#xff0c;打开&#xff09; 输入terminal&#xff0c;找到bell duration&#xff0c;设置成1000。 参考…

95、Spring Data Redis 之使用RedisTemplate 实现自定义查询 及 Spring Data Redis 的样本查询

Spring Data Redis 之使用RedisTemplate 实现自定义查询 Book实体类 原本的接口&#xff0c;再继承我们自定义的接口 自定义查询接口----CustomBookDao 实现类&#xff1a;CustomBookDaoImpl 1、自定义添加hash对象的方法 2、自定义查询价格高于某个点的Book对象 测试&a…

网络机顶盒哪个好?达人分享最新网络电视机顶盒排名TOP5

看视频、网游戏、上网课等等功能网络机顶盒都能实现&#xff0c;可以说是我们使用频率最高的了&#xff0c;尤其是对老人小孩来说。我每年都会进行上百次测评&#xff0c;网络机顶盒就是其中品类之一&#xff0c;很多朋友都在私信我不知道网络机顶盒哪个好&#xff0c;跟着我一…

bert入门

bert是什么 BERT&#xff08;Bidirectional Encoder Representations from Transformers&#xff09;是一种自然语言处理&#xff08;NLP&#xff09;中的预训练模型&#xff0c;它是基于Transformer架构的一种深度学习模型。BERT的主要目标是在大规模文本语料库上进行预训练&a…

3D 生成重建004-DreamFusion and SJC :TEXT-TO-3D USING 2D DIFFUSION

3D 生成重建004-DreamFusion and SJC &#xff1a;TEXT-TO-3D USING 2D DIFFUSION 文章目录 0 论文工作1 论文方法1.1论文方法1.2 CFG1.3影响1.4 SJC 2 效果 0 论文工作 对于生成任务&#xff0c;我们是需要有一个数据样本&#xff0c;让模型去学习数据分布 p ( x ) p(x) p(x…

C++ 获取文件创建时间、修改时间、大小等属性

简介 获取文件创建时间、修改时间、大小等属性 代码 #include <iostream> #include <string.h> #include <time.h>void main() {std::string filename "E:\\LiHai123.txt";struct _stat stat_buffer;int result _stat(filename.c_str(), &s…

Spring Data Redis使用方式

1.导入Spring Data Redis的maven坐标 pom.xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> 2. 配置Redis数据源 2.1application.yml文件…

TLB、页表不命中后发生了什么

问题 下图中真题&#xff08;第三小问的“需要读TLB多少次”&#xff09;的答案是查TLB不命中后会再查一次TLB&#xff08;红色字体是王道的解析&#xff09;&#xff0c;而王道书里给出的地址变换流程图没有这个再查一次TLB的步骤。这要以哪个为准呢。 其实我一开始的理解是&…

手撕各种排序

> 作者简介&#xff1a;დ旧言~&#xff0c;目前大一&#xff0c;现在学习Java&#xff0c;c&#xff0c;c&#xff0c;Python等 > 座右铭&#xff1a;松树千年终是朽&#xff0c;槿花一日自为荣。 > 目标&#xff1a;掌握每种排序的方法&#xff0c;理解每种排序利弊…

Tomcat隔离web原理和热加载热部署

Tomcat 如何打破双亲委派机制 Tomcat 的自定义类加载器 WebAppClassLoader 打破了双亲委派机制&#xff0c;它首先自己尝试去加载某个类&#xff0c;如果找不到再代理给父类加载器&#xff0c;其目的是优先加载 Web 应用自己定义的类。具体实现就是重写 ClassLoader 的两个方法…

bootstrapjs开发环境搭建

Bootstrapjs是一个web前端页面应用开发框架&#xff0c;其提供功能丰富的JavaScript工具集以及用户界面元素或组件的样式集&#xff0c;本文主要描述bootstrapjs的开发环境搭建。 如上所示&#xff0c;使用nodejs运行时环境、使用npm包管理工具、使用npm初始化一个项目工程test…

红队专题-Cobalt strike4.5二次开发

红队专题 招募六边形战士队员IDEA 自动换行原版CS反编译破解jar包反编译拔掉暗桩初始环境效果 stageless beacon http通信协议 过程分析上线&心跳get请求teamserver 处理请求 参考链接 招募六边形战士队员 一起学习 代码审计、安全开发、web攻防、逆向等。。。 私信联系 …

MES系统安灯管理:实时可视化生产线状态

一、MES系统安灯管理的意义&#xff1a; 安灯管理是指通过使用不同颜色的灯光信号来表示生产线的状态&#xff0c;以便生产人员能够直观地了解生产线的运行情况。MES系统安灯管理的意义在于提供一个实时可视化的工具&#xff0c;使制造企业能够及时发现生产线异常和潜在问题&a…

UE5中实现沿样条线创建网格体2-SplineMesh版本

我在之前的一篇文章中写过沿样条线创建网格体的方法&#xff1a; https://blog.csdn.net/grayrail/article/details/130453733 但该方法没有网格变形操作&#xff0c;就会导致每一段网格对象是无法连接的&#xff1a; 后来发现了SplineMesh方法可以比较好的解决这个问题&…

【K8S系列】深入解析k8s 网络插件—kube-router

序言 做一件事并不难&#xff0c;难的是在于坚持。坚持一下也不难&#xff0c;难的是坚持到底。 文章标记颜色说明&#xff1a; 黄色&#xff1a;重要标题红色&#xff1a;用来标记结论绿色&#xff1a;用来标记论点蓝色&#xff1a;用来标记论点 在现代容器化应用程序的世界中…

横向AlGaN/GaN基SBD结构及物理模型数据库的开发

GaN基功率器件凭借其临界电场高、电子饱和漂移速度大、热导率高等优良性能在大功率快充、充电桩、新能源汽车等领域具备广泛应用空间。为进一步助推半导体高频、高功率微电子器件的发展进程&#xff0c;天津赛米卡尔科技有限公司技术团队依托先进的半导体TCAD仿真平台成功开发出…

【WebService】C#搭建的标准WebService接口,在使ESB模版作为参数无法获取参数数据

一、问题说明 1.1 问题描述 使用C# 搭建WebService接口&#xff0c;并按照ESB平台人员的要求&#xff0c;将命名空间改为"http://esb.webservice",使用PostmanESB平台人员提供的入参示例进行测试时&#xff0c;callBussiness接口参数message始终为null。 以下是ES…