字符函数与字符串函数

目录

 一.字符分类函数

二.字符转化函数 

三.strlen函数 

函数的介绍

strlen函数的模拟实现

1.计算器法

2.递归 

三.指针-指针的方式 

 四.strcpy函数

 函数介绍

strcmp的模拟实现

五.strcat函数 

函数介绍

strcat的模拟实现

六.strcmp函数

函数介绍 

返回值

strcmp函数的模仿实现

七.strncpy,strncat,strncmp函数 

(1)strncpy 

(2)strncat

(3)strncmp函数

注意事项

八.strstr函数

函数的介绍

返回值

strstr函数的模拟实现

九.strtok函数

 十.strerror函数

错误码

perror函数


 

 一.字符分类函数

C语言中有一系列函数是专门做字符分类的,也就是一个字符是属于什么类型的字符的。

这些函数的使用都需要包含一个头文件是ctype.h

函数作用
isalnum检查一个字符是否是字母或数字
isalpha检查一个字符是否是字母
islower检查一个字符是否是小写字母
isupper检查一个字符是否是大写字母
isdigit检查字符是否为数字
isxdigit检查一个字符是否是十六进制的字符
iscntrl检查一个字符是否是控制字符
isgraph

检查一个字符是否是图形字符

isspace检查一个字符是否是空白字符
isblank检查一个字符是否是空格字符
isprint检查一个字符是否是可打印字符
ispunct检查一个字符是否是标点字符

参考网址: 空终止字节字符串 - cppreference.comicon-default.png?t=N7T8https://zh.cppreference.com/w/c/string/byte

#include<ctype.h>
#include<stdio.h>
int main()
{int ret = isalnum('a');printf("%d",ret);return 0;
}

 这些函数的的参数都是是int类型的,即使我们传过去的是字符,但是其实是根据它的ASCII值进行计算的。返回类型是int,如果是就会返回一个非零的数字,比如上述代码返回值就是2.(这可能有所差异),但一定是非零值,如果不是上述的所有函数都会返回0.

练习:写一个代码将字符串中的小写转化为大写,其他字符不变。

int main()
{char str[] = { "Hello World!" };int i = 0;char c;while (str[i]) {c = str[i];if (islower(c)) {c -= 32;}i++;putchar(c);}
}

 这里-32的原因是因为ASCII值中a的值是97,A的值是65,也就是所小写字母减32就是大写字母。通过这一点我们就可以知道如何进行大小写的转化。

二.字符转化函数 

ctype:<cctype> (ctype.h) - C++ Reference

 字符转化函数只有两种;

函数功能
toupper将小写字符转化为大写的
tolower将大写字符转化为小写的

这两个函数的参数也是int 返回值也是int,返回值返回的就应该是该字符对应ASCII值。

如果这个字符无法转化,就只会返回原字符。

所以上述的题目就可以改写为: 

int main()
{char str[] = { "Hello World!" };int i = 0;char c;while (str[i]) {c = str[i];if (islower(c)) {c = toupper(c);}i++;putchar(c);}
}

注意传递的参数只能是一个字符,而不能是字符串 

三.strlen函数 

函数的介绍

 函数原型:

size_t strlen(const char* str);

函数功能是返回字符串的长度。字符串的长度是有'\0'字符决定,它之前的字符个数就是字符串的长度。C字符串的长度等于字符串开头和'\0'字符之间的字符数.

注:字符串长度是不包括'\0'的

 注意字符串的长度是与包含字符串的数组的大小不一样的。

int main()
{char str[] = "abcdef";return 0;
}

通过调试,我们可以发现,这个数组的类型是char[7]说明这个数组大小是7,但是字符串的长度是6。

所有我们如果给【】中加上值,这个值的大小至少要是7,不能比7小不如就存不下这个字符串。

那么多余的值就会被初始化为'\0'.

字符串以'\0'作为结束的标志,strlen返回的是'\0'之前的字符个数,不包含'\0'.

注意函数的返回值是size_t类型的是无符号的。

strlen的使用需要包含头文件<string.h>

strlen函数的模拟实现

1.计算器法

#include<assert.h>
#include<stdio.h>
size_t my_strlen(const char* str)
{int count = 0;assert(str);while (*str) {count++;str++;}return (size_t)count;
}

2.递归 

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

递归相比其他的方法是没有创建临时变量。 

三.指针-指针的方式 

回顾:指针减指针得到的是指针之间的元素个数+1,比如第五个字符的地址-第一个字符的地址就是4(中间有三个元素+1);

size_t my_strlen(const char* str) {assert(str);char* start = str;while (*str != '\0') {str++;}return str - start;
}

注意这里必须是高地址减低地址,不然就是负数了。 

 四.strcpy函数

 函数介绍

strcpy是用于字符串拷贝的,第一个参数destination是目的地的地址,而第二个参数是源头的地址。

#include<stdio.h>
#include<string.h>
int main()
{char dest[10] = "abc";char* source = "abcdef";strcpy(dest,source);printf("%s",dest);return 0;
}

这样我们就把source所指向的字符串成功复制到了dest字符数组中。

注意事项:

  1. 字符串在拷贝的过程中是包含字符'\0'的,而且会在这个字符停止拷贝。也就是所源字符串必须以'\0'结尾。
  2. 为了避免溢出,目标空间必须足够大,要确保能完整存放源字符串。
  3. 目标空间必须能够修改。
  4. 目标空间在内存上不能与源字符串相互重叠。

返回值是目标空间的地址。 

strcmp的模拟实现

#include<stdio.h>
#include<string.h>char* my_strcpy(char* destination,const char* source)
{char* ret = destination;while (*source){*destination++ = *source++;}*destination = *source;//最后将'\0'复制return ret;
}
int main()
{char dest[10] = "abcxxxxxxx";char* source = "abcdef";my_strcpy(dest,source);printf("%s",dest);return 0;
}

简化一下:

char* my_strcpy(char* destination, const char* source)
{char* ret = destination;while (*destination++ = *source++){;}return ret;
}

五.strcat函数 

函数介绍

strcat这个函数是用于字符串附加的,将source所指向的字符串附加到destination的末尾。

int main()
{char dest[20] = "hello ";char* source = "world!";strcat(dest,source);printf("%s",dest);return 0;
}

也就是将source的字符串链接在dest的后面。我们知道字符串的末尾是有一个隐藏的'\0'的,但是这里还是打印出了world!,说明在使用strcat进行附加的时候,末尾的'\0'是已经被覆盖了的。

注意事项:

  1. destination所指向的字符串最后的'\0'字符被source所指向的字符串的第一个字符覆盖了。
  2. 这个新的字符串末尾是含有'\0'的。
  3. 目标空间必须足够大,要能容纳新的字符串。
  4. destination和source所指向的空间在内存上不能重叠,否则运行结果是未知的。

返回值的是destination的起始地址。

strcat的模拟实现

 首先我们需要先找到目标字符末尾的'\0'字符,然后进行拷贝。

#include<stdio.h>
#include<string.h>
char* my_strcat(char* destination,const char* source)
{char* ret = destination;while (*destination)destination++;while (*destination++ = *source++);return ret;
}
int main()
{char dest[20] = "hello ";char* source = "world!";my_strcat(dest,source);printf("%s",dest);return 0;
}

六.strcmp函数

参考网址:strcmp - C++ Referenceicon-default.png?t=N7T8https://legacy.cplusplus.com/reference/cstring/strcmp/?kw=strcmp

函数介绍 

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

This function starts comparing the first character of each string. If they are equal to each other, it continues with the following pairs until the characters differ or until a terminating null-character is reached.This function performs a binary comparison of the characters.

这个函数是用于比较两个字符串的。这个函数首先会比较两个字符串的首字符,如果相同,这个函数就会继续比较下一对字符,直到遇到不同的字符或者'\0'.这个函数的执行的是二进制的比较。

返回值

情况1: 

"abc"//str1
"abf"//str2

这两个字符串的前两个字符相同,而第三个字符不同,c的ASCII值小于f的ASCII值。所以strcmp会返回一个小于0的整形。

 情况2:

"abc"//str1
"abc"//str2

这两个字符串一模一样,这样就会strcmp就会返回一个0。

情况3:

"abc"//str1
"abb"//str2

这两个字符串的前两个字符相同,而第三个字符不同,c的ASCII值大于b的ASCII值。所以strcmp会返回一个大于0的整形。 

情况4:

"abc"//str1
"aaz"//str2

 这两个字符串的第一个字符相同,而第二个字符str1大于str2,这时候返回一个大于0的整形。第三个字符就不会进行比较了。

strcmp函数的模仿实现

#define _CRT_SECURE_NO_WARNINGS 
#include<stdio.h>
#include<string.h>
#include<assert.h>
int my_strcmp(const char* str1, const char* str2)
{assert(str1 && str2);int ret = 0;while (*str1 == *str2){if (*str1 == '\0'){return 0;}str1++;str2++;}return *str1 - *str2;
}
int main()
{char* str1 = "abc";char* str2 = "aaz";printf("%d",my_strcmp(str1,str2));return 0;
}

七.strncpy,strncat,strncmp函数 

这三个函数与上面所讲的三个函数非常的相似,从函数民上我们发现只多了一个n字母。

那么实际上他们有什么区别呢?

char* strncpy(char* destination, const char* source, size_t num);
char* strncat(char* destination, const char* source, size_t num);
int strncmp(const char* str1, const char* str2, size_t num);

从他们的参数上来看,我们发现只多了一个size_t类型的参数num,也就是函数名中多的n。

那么这个num到底有什么用呢?

(1)strncpy 

对于strncpy来说,这个num就是用于指定字数的字符串拷贝。

int main()
{char dest[20] = "xxxxxxxxxxhello ";char* source = "world!";strncpy(dest,source,5);printf("%s",dest);return 0;
}

打印结果:

仔细观察发现source中的感叹号并没有复制到dest字符中。也就是只复制了source的前五个字符。所以这个num就是给我们来控制想复制的字符个数的。

如果num的值大于source所指向的字符串的长度 。会在拷贝完字符串后,在'\0'的后面一直追加0,直到满足num个字符。

模拟实现

#include<stdio.h>
char* my_strncpy(char* destination, const char* source, size_t num)
{char* ret = destination;for (int i = num; i > 0; i--){if (*source) {*destination = *source;}else {*destination = '\0';}destination++;source++;}return ret;
}
int main()
{char str1[20] = "xxxxx world!!!";char* str2 = "hello";printf("%s\n", str1);my_strncpy(str1,str2,7);printf("%s",str1);return 0;
}

(2)strncat

这个函数同理,num也是用于控制附加的个数的。

int main()
{char dest[20] = "hello ";char* source = "world!";strncat(dest,source,5);printf("%s",dest);return 0;
}

 虽然我们只附加了五个字符,这个是并没有包含'\0'的,所以这个函数会自动在新字符串的末尾补上斜杠零。

strcat的模拟实现

char* my_strncat(char* destination, const char* source, size_t num)
{char* ret = destination;while (*destination){destination++;}for (int i = 0; i < num; i++){if (*source) {*destination = *source;}else{*destination = '\0';}destination++;source++;}return ret;
}
int main()
{char str1[20] = "hello ";char* str2 = "world!!!";printf("%s\n", str1);my_strncat(str1, str2, 7);printf("%s", str1);return 0;
}

(3)strncmp函数

这个函数也是,num控制比较的个数。只比较前num个字符,其他均与strcmp一样

注意事项

这六个函数的目标字符串和源字符串都不能在内存上有任何重叠,不然运行结果是未知的,可能复合要求,也可能不符合,这与编译器有关。

八.strstr函数

函数的介绍

参考网址:strstr - C++ Reference

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

 这个函数是用于在一个字符串中查找是否具有另一个字符串。

返回值

这个函数的返回值是str1中具有str2字符串的首字符的地址。

比如:"abcdef"和"cdef".返回值就是“abcdef”中字符C的地址。

如果没有找到,就会返回NULL.

strstr函数的模拟实现

#include<stdio.h>
#include<string.h>
char* my_strstr(const char *str1,const char*str2)
{if (!*str2)return (char*)str1;char* s1 = NULL;char* s2 = NULL;char* cur = (char*)str1;while (*cur){s1 = cur;s2 = (char*)str2;while (*s1 == *s2 && *s1 && *s2){s1++;s2++;}if (!*s2)return cur;cur++;}return NULL;
}
int main()
{char* str1 = "this is a string";char* str2 = "str";char * ret = my_strstr(str1,str2);printf("%s\n",ret);return 0;
}

九.strtok函数

char* strtok(char* str, const char* delimiters);

这个函数是用于字符串的分隔的。

str指针所指向的是一个需要分隔的字符串。

delimiters所指向的是分隔符的合集。

#include<stdio.h>
#include<string.h>
int main()
{char str[20] = "abc.hello_world/no";char* del = "._/";//第一次分隔char* first =  strtok(str,del);printf("%s\n",first);//第二次分隔char*second = strtok(NULL,del);printf("%s\n",second);//第三分隔char* third = strtok(NULL,del);printf("%s\n",third);return 0;
}

打印结果 

最终我们发现str这个字符数组被分割了,而这个del就是分隔符的合集。这个函数会根据分隔符合集中的每一个字符分割这个字符串,并在新的字符串结尾加上'\0',以免读取时造成溢出的问题。 

每一次调用这个函数只会分隔一次,并且返回初始地址,下一次调用的时候(如果还能进行分隔)只需要传NULL和分隔符。如果读取到了str中的‘\0’就会停止分隔了。并且后续如果继续调用,只会返回NULL.

我们也可以使用for循环的方式对分隔后的字符串进行打印。

#include <stdio.h>
#include <string.h>int main()
{char arr[] = "192.168.6.111";char* sep = ".";char* str = NULL;for (str = strtok(arr, sep); str != NULL; str = strtok(NULL, sep)){printf("%s\n", str);}return 0;
}

 十.strerror函数

char* strerror(int errnum);

strerror函数可把参数部分错误码所对应的错误信息所对应的字符串的地址返回。

错误码

在不同的系统和C语言标准库中都规定了一些错误码,一般是放在<errno.h> 头文件中的,C语言程序的启动时候,就会使用一个全面的变量errno来记录程序的当前的错误码,只不过程序启动的时候errno是0,表示没有错误,当我们在使用标准库中的函数的时候发生了某种错误,就会将对应的错误码放在errno中,而一个错误码的数字是整数,这是比较难以理解的,所以每一个错误码都是由对应的错误信息的,strerror这个函数就是用于将错误码对应的错误信息对应的字符串地址返回。

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

 通过上面的代码我们就可以依次打印出错误信息了。

在Windows11和VS2022的环境下输出结果是:

举例:

#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));return 0;
}

 这个fopen就是用于打开文件的,后续我们会继续了解。

但是我们运行这串代码会发现给出的错误信息是:

这个错误信息的意思是:没有这样的文件或者文件夹,也就是我们打开文件失败了。

perror函数

#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));perror("Error opening file unexist.ent");return 0;
}

 我们也可以使用perror这个函数,这个函数会先打印这个参数部分的字符串,然后就是一个冒号和空格,最后是错误信息。

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

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

相关文章

Mysql学习--深入探究索引和事务的重点要点与考点

꒰˃͈꒵˂͈꒱ write in front ꒰˃͈꒵˂͈꒱ ʕ̯•͡˔•̯᷅ʔ大家好&#xff0c;我是xiaoxie.希望你看完之后,有不足之处请多多谅解&#xff0c;让我们一起共同进步૮₍❀ᴗ͈ . ᴗ͈ აxiaoxieʕ̯•͡˔•̯᷅ʔ—CSDN博客 本文由xiaoxieʕ̯•͡˔•̯᷅ʔ 原创 CSDN …

自动驾驶---Motion Planning之轨迹Path优化

1 背景 在之前的几篇文章中,不管是通过构建SL图《自动驾驶---Motion Planning之Path Boundary》,ST图《自动驾驶---Motion Planning之Speed Boundary》,又或者是构建SLT图《自动驾驶---Motion Planning之构建SLT Driving Corridor》,最终我们都是为了得到boundary的信息。 …

OpenCV实战:智能人脸识别打卡系统

1、介绍 目前&#xff0c;很多公司对员工的考勤同时通过打卡机或者钉钉之类的打卡软件&#xff0c;传统的员工打卡方式有很多&#xff0c;例如点名、签字、刷卡、指纹等等 然而随机机器视觉、计算机视觉的不断发展&#xff0c;算力的不断提升&#xff0c;人工智能对人脸检测的…

如何用联合(共用体)union验证系统大小端

一&#xff1a;思路 由联合体的特点&#xff0c;可知上图&#xff0c;char c 和 int i 共用四个字节&#xff0c;假设是小端&#xff0c;则由左到右是低地址到高地址&#xff0c;四个字节的内容如图所示01 00 00 00 代码展示&#xff1a; 如果第一个字节是1&#xff0c;则证明…

<Linux> 线程池

目录 前言&#xff1a; 一、线程池概念 &#xff08;一&#xff09;池化技术 &#xff08;二&#xff09;优点 &#xff08;三&#xff09;应用场景 二、线程池的实现 &#xff08;一&#xff09;线程池_V1&#xff08;朴素版&#xff09; &#xff08;二&#xff09;线…

Nginx发布之后可以使用IP访问,不能使用localhost访问, Nginx发布之后可以使用localhost访问,不能使用IP访问,

如标题所说 Nginx发布之后可以使用IP访问&#xff0c;不能使用localhost访问&#xff0c; Nginx发布之后可以使用localhost访问&#xff0c;不能使用IP访问&#xff0c; 修改配置文件也没有用 清除浏览器缓存数据

配置DHCPV6

组网需求 如果大量的企业用户IPv6地址都是手动配置&#xff0c;那么网络管理员工作量大&#xff0c;而且可管理性很差。管理员希望实现公司用户IPv6地址和网络配置参数的自动获取&#xff0c;便于统一管理&#xff0c;实现IPv6的层次布局。 图1 DHCPv6服务器组网图 配置思路 …

自动化改变金融科技文档生命周期

金融科技公司可能处于软件开发的最前沿&#xff0c;但即使是最先进的系统也必须能够支持金融服务领域采用的一系列文档密集型程序。因此&#xff0c;绝大多数金融科技企业都使用数字文档管理解决方案&#xff0c;无论是内部构建的还是由第三方供应商开发的。金融科技公司可以通…

UI自动化测试面试题小结

列举web自动化中常见的元素定位方式&#xff1f; id&#xff1a;根据id来获取元素&#xff0c;返回单个元素&#xff0c;id值一般是唯一的&#xff1b; name&#xff1a;根据元素的name属性定位&#xff1b; tagName&#xff1a;根据元素的标签名定位&#xff1b; className…

深入理解XML技术(看这一篇就够了)

目录&#xff1a; XMLXML的功能XML基本语法XML组成部分约束DTD约束Schema约束 Jsoup解析器DOMSAXXML常见解析器DOM4JJsoupXPath解析 XML 概念 XML&#xff08;Extensible Markup Language&#xff09;&#xff1a;可扩展标记语言 可扩展&#xff1a;标签都是自定义的。 发展历…

[ C++ ] STL---仿函数与priority_queue

目录 仿函数 示例一&#xff1a; 示例二 : 常见的仿函数 priority_queue简介 priority_queue的常用接口 priority_queue的模拟实现 基础接口 push() 堆的向上调整算法 堆的插入 pop() 堆的向下调整算法 堆的删除 priority_queue最终实现 仿函数 仿函数&#xff…

MusicHiFi: Fast High-Fidelity Stereo Vocoding

MusicHiFi: Fast High-Fidelity Stereo Vocoding 相关链接&#xff1a;arxiv github 关键字&#xff1a;音乐生成、高保真立体声、立体声编解码器、生成对抗网络、频带扩展 摘要 MusicHiFi是一种高效的高保真立体声编解码器&#xff0c;它通过将低分辨率的mel频谱图转换为音频…

【Vue】Vue集成Element-UI框架

&#x1f64b;‍ 一日之际在于晨 ⭐本期内容&#xff1a;Vue集成Element-UI框架 &#x1f3c6;系列专栏&#xff1a;从0开始的Vue之旅 文章目录 Element-UI简介安装Element-UInpm安装CDN安装 引入Element-UI测试是否引入成功总结 Element-UI简介 Element-UI官网&#xff1a;点…

极大提高工作效率的 Linux 命令

作为一名软件开发人员&#xff0c;掌握 Linux 命令是必不可少的技能。即使你使用 Windows 或 macOS&#xff0c;你总会遇到需要使用 Linux 命令的场合。例如&#xff0c;大多数 Docker 镜像都基于 Linux 系统。要进行 DevOps 工作&#xff0c;你需要熟悉Linux&#xff0c;至少要…

vue-quill-editor和vue-ueditor-wrap富文本编辑器应用

目录 一、vue-quill-editor 1.1、界面展示 1.2、代码介绍 1.2.1、安装 1.2.2、配置 1.2.3、代码应用 1.2.4、提取内容 二、vue-ueditor-wrap 2.1、界面展示 2.2、代码介绍 2.2.1、安装 2.2.2、配置 2.2.3、代码应用 一、vue-quill-editor 1.1、界面展示 文本输出…

Vue响应式原理全解析

前言 大家好&#xff0c;我是程序员蒿里行。浅浅记录一下面试中的高频问题&#xff0c;请你谈一下Vue响应式原理。 必备前置知识&#xff0c;​​Vue2​​官方文档中​​深入响应式原理​​​及​​Vue3​​官方文档中​​深入响应式系统​​。 什么是响应式 响应式本质是当…

liunx CentOS7 搭建lnmp环境 php nginx mysql

安装一些刚需软件&#xff1a;不懂请自行查询 安装一些需要的软件命令 yum install wget vim net-tools bash* lrzsz tree nmapnc lsof telnet -y 刷新命令 source /usr/share/bash-completion/bash_completion echo source /usr/share/bash-completion/bash_completion &…

UE5 C++ 3D血条 响应人物受伤 案例

一.3Dwidget 1.创建C Userwidget的 MyHealthWidget&#xff0c;声明当前血量和最大血量 UCLASS() class PRACTICEC_API UMyHealthWidget : public UUserWidget {GENERATED_BODY() public:UPROPERTY(EditAnywhere,BlueprintReadWrite,Category "MyWidget")float C…

利用API打造卓越的用户体验

&#x1f34e;个人博客&#xff1a;个人主页 &#x1f3c6;个人专栏&#xff1a;日常聊聊 ⛳️ 功不唐捐&#xff0c;玉汝于成 目录 正文 1. 数据驱动的设计 2. 功能扩展与整合 3. 实时性与响应性 4. 个性化推荐与定制化服务 结语 我的其他博客 正文 随着数字化时代的…

npm i安装依赖报错,但是cnpm i 却安装成功

问题描述&#xff1a;在a项目中npm i 安装依赖时发生以上报错&#xff0c;但是cnpm i 却成功&#xff0c;而且在其他项目中npm i 安装其他项目依赖也能成功.... 解决办法&#xff1a;删除项目中package-lock.json文件后再npm i 即可