【C进阶】第十四篇——字符串函数

news2024/11/29 11:45:50

strlen - 求字符串长度

函数介绍

模拟实现(三种方式)

strcpy - 字符串拷贝

函数介绍

模拟实现

strcat - 字符串追加

函数介绍

模拟实现

strcmp - 字符串比较

函数介绍

模拟实现

strstr - 字符串查找

函数介绍

模拟实现

strtok - 字符串分割

函数介绍

strerror - 错误报告函数

函数介绍

perror - 错误报告函数

函数介绍

strncpy - 限制操作长度

函数介绍

strncmp - 限制操作长度

函数介绍

strncat - 限制操作长度

函数介绍


strlen - 求字符串长度

函数介绍

size_t strlen( const char *string );

strlen函数是一个用于求字符串长度的库函数,它的参数是被求长度的字符串的起始地址,返回值是一个无符号整型.

注意:

  • 参数指向的字符串要以'\0'结束
  • strlen返回的是在字符串中'\0'之前出现的字符个数(不包含'\0')
  • 注意函数的返回值为size_t,是无符号的(易错)

举个例子,比如我们要求字符串"abcded"的长度

#include<stdio.h>
#include<string.h>
int main()
{
	char arr[] = "abcdef";
	size_t ret = strlen(arr);
	printf("%d", ret);
	return 0;
}

 运行结果:

模拟实现(三种方式)

方式一:计数器的方式

size_t my_strlen1(const char* str)
{
	size_t count = 0;//计数器
	while (*str)
	{
		count++;
		str++;
	}
	return count;
}

方式二:递归的方式

我们一进入函数体就判断传入指针指向的内容是否为’\0’,如果是就返回0,不是就返回1+my_strlen2(str+1),如此进行下去,直到递归到内层时找到’\0’,这时再一步步将值返回回来即可。

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

方式三:指针-指针的方式

进入函数体时,我们事先定义一个指针变量将传入的指针保存下来,然后将传入的指针向后移,直到遇到’\0‘时,我们返回当前指针与保存的指针的差值即可。

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

strcpy - 字符串拷贝

函数介绍

char *strcpy(char *Destination,const char *Source);

 stcpy函数是一个用于拷贝字符串的函数,即将一个字符串中的内容拷贝到另一个字符串中(会覆盖掉原字符串内容)。它的参数是两个指针,第一个指向的是拷贝字符串的目的地的起始位置,即要将字符串拷贝到什么地方;第二个指向的是要拷贝字符串的内容的起始位置,即需要拷贝的字符串。它的返回值是目标空间的起始位置。

注意:

  • 源字符串(需要被拷贝的字符串)必须以'\0’结束。
  • 会被源字符串中的‘\0’一同拷贝到目标空间
  • 目标空间必须足够大,以确保能存放源字符串。
  • 目标空间必须可变。

代码示例:

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

运行结果:"abc"被"def"覆盖了。

模拟实现

进入函数体时先定义一个指针变量保存目标空间的起始位置,便于之后返回。然后将源字符串中的字符--赋值给目标空间,直到遇到源字符串中的'\0',将‘\0‘也赋值给目标空间后结束赋值,并返回目标空间的起始位置。

char* my_strcpy(char *dest, char *src)
{
	char* ret = dest;
	assert(dest != NULL && src != NULL);
	while (*dest++ = *src++)
	{
		;
	}
	return ret;
}

strcat - 字符串追加

函数介绍

char *strcat(char *Destination,const char *Source);

strcat函数是一个用于追加字符串的函数,即将一个字符串中的内容追加到另一个字符串后面(不会覆盖原字符串内容)。它的参数是两个指针,第一个指向的是追加字符串的目的地的起始位置,即要将字符串追加到什么地方;第二个指向的是要追加字符串的内容的起始位置,即需要追加的字符串。它的返回值是目标空间的起始位置。
注意:

  • 源字符串必须以'\0’结束。
  • 目标空间必须足够大,能容纳下源字符串的内容。
  • 目标空间必须可修改。
  • 字符串不能给自己追加(‘\0’被覆盖,无终止条件)

我们要将arr2数组中的"csdn!"追加到arr1数组的后面。

int main()
{
	char arr1[10] = "abc";
	char arr2[] = "def";
	strcat(arr1, arr2);
	printf("%s", arr1);
	return 0;
}

运行结果:

模拟实现

进入函数体依然先定义一个指针变量用于存放目标空间的起始位置,便于之后返回。然后用循环先找到目标空间的’\0’,之后从’\0’的位置开始追加源字符串的内容,直到追加到源字符串中的’\0’为止。最后返回目标空间的起始位置。

char* my_strcat(char* dest, char* src)
{
	assert(dest != NULL && src != NULL);
	char* ret = dest;
	while (*dest!='\0')
	{
		dest++;
	}
	while (*dest++ = *src++)
	{
		;
	}
	return ret;
}

strcmp - 字符串比较

函数介绍

int strcmp( const char *string1, const char *string2 );

 strcmp函数是一个用于比较两个字符串内容的函数。它的参数是两个指针,指针分别指向两个待比较字符串的起始位置。它的返回值是一个整型数字。当string1大于string2的时候返回一个大于0的数;当string1等于string2的时候返回0;当string1小于string2的时候返回一个小于0的数。
注意:

  • 字符串比较的不是字符串长度的大小,而是两个字符串中对应位置字符的ASCII值。

比如比较字符串"hello world!"和字符串"hello csdn!"的大小。

int main()
{
	char arr1[20] = "hello world!";
	char arr2[20] = "hello csdn!";
	int ret = strcmp(arr1, arr2);
	printf("%d", ret);
	return 0;
}

运行结果:比较字符串的时候发现前面字符的ASCll值都相同,知道比较到字符‘w'和字符'c'时,发现字符'w'的ASCll值大于字符'c'的ASCII值,于是返回一个大于0的数。

模拟实现

进入函数体直接比较起始位置的字符的大小。如果相同并且不为’\0’那么继续比较下一对字符的大小;如果相同并且为’\0’那么说明字符串比较完毕,那么直接返回0;如果不同则直接返回str1与str2中对应字符的ASCII值的差值(当str1中对应字符大于str2中的对应字符时返回正值,当str1中对应字符小于str2中的对应字符时返回负值)。

int my_strcmp(const char* str1, const char* str2)
{
	assert(str1 != NULL);//断言,str1为空指针时报错
	assert(str2 != NULL);//断言,str2为空指针时报错
	while (*str1 == *str2)
	{
		if (*str1 == '\0')//字符串全部比较完毕
			return 0;
		str1++;
		str2++;
	}
	return *str1 - *str2;
}

strstr - 字符串查找

函数介绍

char *strstr( const char *string, const char *strCharSet );

 strstr函数可以在一个字符串(字符串1)中查找另一个字符串(字符串2),如果字符串2存在于该字符串1中,那么就返回被字符串2在字符串1中第一次出现的起始位置,如果在字符串1中找不到字符串2,那么就返回空指针(NULL)。它的第一个参数是字符串1的起始位置,第二个参数是字符串2的起始位置。
注意:

  • 若字符串2为空字符串,则返回字符串1的起始位置。

举个例子,比如我们在字符串"abcdefbcd"中查找字符串"bcd"。

#include<stdio.h>
#include<string.h>
int main()
{
	char arr1[] = "abcdefbcd";
	char arr2[] = "bcd";
	char* ret = strstr(arr1, arr2);//在arr1中查找arr2字符串第一次出现的位置
	if (ret != NULL)
		printf("%s\n", ret);
	else
		printf("找不到\n");
	return 0;
}

运行结果:

 

 注意:strstr函数的返回值是字符串"bcd"在字符串"abcdefbcd"中第一次出现的位置的起始位置,而不是出现几次就返回几个起始位置。

模拟实现

strstr函数的模拟实现相对复杂,在实现过程中我们需要设置3个指针变量来辅助实现函数功能。
cp指针: 记录每次开始匹配时的起始位置,当从该位置开始匹配时就找到了目标字符串,便于返回目标字符串出现的起始位置;当从该位置开始没有匹配成功时,则从cp++处开始下一次的匹配。
p1和p2指针: 通过判断p1和p2指针解引用后是否相等来判断每个字符是否匹配成功,若成功,则指针后移比较下一对字符;若失败,p1指针返回cp指针处,p2指针返回待查找字符串的起始位置。

例如,在字符串"abbbcdef"中查找字符串"bbc":
刚刚开始时3个指针的指向如图所示:

 若p1与p2匹配不成功,则cp指针后移,接着将cp指针赋值给p1指针:

 此时,p1与p2匹配成功,那么cp指针不动,p1和p2指针后移继续比较:

 当p1与p2匹配不成功时,cp指针后移一位,p1返回cp位置,p2返回待查找字符串起始位置:

 从此位置开始下一轮的比较:

 直到当p2指向的内容为\0时,便说明待查找字符串中的字符已经被找完,也说明了从当前cp位置开始匹配能够找到目标字符串,所以此时返回指针cp即可。

char* my_strstr(const char* str1, const char* str2)
{
	assert(str1 != NULL);//断言,当str1为空指针报错
	assert(str2 != NULL);//断言,当str2为空指针报错
	const char* cp = str1;//记录开始匹配时的起始位置
	if (*str2 == '\0')//要查找的字符串为空字符串
		return (char*)str1;
	while (*cp)
	{
		const char* p1 = cp;
		const char* p2 = str2;
		while ((*p1!='\0') && (*p2!='\0') && (*p1 == *p2))
		{
			p1++;
			p2++;
		}
		if (*p2 == '\0')//目标字符串已被查找完
			return (char*)cp;
		cp++;
	}
	return NULL;//找不到目标字符串
}

strtok - 字符串分割

函数介绍

char *strtok( char *strToken, const char *strDelimit );

 strtok函数能通过给定的一系列字符将一个字符串分割成许多子字符串的函数。它的第一个参数是需要被分割的字符串的首地址;第二个参数是一个字符串的首地址,该字符串是用作分隔符的字符集合。返回值是查找到的标记的首地址。
注意:

  • strtok函数找到strToken中的一个标记时,会将其用 \0结尾并返回这个标记的首地址
  • strtok函数会改变strToken函数,所以在使用strtok函数切分的字符串都是临时拷贝的内容并且可修改。
  • strtok函数的第一个参数不为NULL时,函数将找到strToken中的第一个标记,并保存它在字符串中的位置。
  • strtok函数的第一个参数为NULL时,函数将从同一个字符串中被保存的位置开始查找它的下一个标记。
  • 若字符串中不存在更多的标记,则返回NULL指针。

举个例子,比如我们要将字符串"2957055542@qq.com"以"@“字符和”."字符分割开。

#include<stdio.h>
#include<string.h>
int main()
{
	char arr1[] = "2957055542@qq.com";//待分割字符串
	char arr2[] = "@.";//分隔符的字符集合
	char arr3[20] = { 0 };
	strcpy(arr3, arr1);//将数据拷贝一份使用,防止原数据被修改
	char* token = strtok(arr3, arr2);//第一次传参需传入待分割字符串首地址
	while (token != NULL)//说明还未分割完
	{
		printf("%s\n", token);
		token = strtok(NULL, arr2);//对同一个字符串进行分割,第二次及以后的第一个参数为NULL
	}
	return 0;
}

 

 注意:当strtok函数找到第一个标记时,将其后的’@‘字符改为’\0’并返回第一个标记的首地址,所以我们以返回的地址为首地址开始打印字符串的时候就只会打印出2957055542,第二次再对该字符串调用strtok函数时将从’@'字符后面开始寻找下一个标记。

strerror - 错误报告函数

函数介绍

char *strerror( int errnum );

strerror函数可以把错误码转换为对应的错误信息,返回错误信息对应字符串的起始地址。

perror - 错误报告函数

函数介绍

void perror( const char *string );

perror函数可以打印一个错误信息,无返回值。

我们需要知道,库函数在使用的时候如果发生错误,都会有对应的错误码,而这些错误码都会被存放在errno这个全局变量中,如果要使用这个全局变量,我们需要引其对应的头文件:#include<errno.h>

举个例子:
注:fopen函数的功能是打开一个文件,当其执行成功时会返回打开文件的首地址,执行失败时会返回一个空指针(NULL)。

#include<stdio.h>
#include<string.h>
#include<errno.h>
int main()
{
	FILE* pf = fopen("test.txt", "r");//打开test.txt文件阅读
	if (pf == NULL)
	{
		printf("%s\n", strerror(errno));
		perror("fopen");
	}
	return 0;
}

运行结果:

 当我们要打开一个不存在的文件(test.txt)来阅读的时候,显然fopen函数会执行失败,于是pf指针接收的便是空指针(NULL)。

strerror: 只负责将错误码转换为对应的错误信息,不打印。
perror: 直接打印错误信息,并且我们可以自己加上注释来明确错误来源于哪个库函数

strncpy - 限制操作长度

函数介绍

char *strncpy( char *Dest, const char *Source, size_t count );

strncpy的参数与strcpy相比较多出了一个参数,而这个参数就是需要被操作的字符个数。
注意:

  • 当操作数小于等于源字符串中的字符个数时,操作数的大小决定被拷贝的字符个数。
  • 当操作数大于源字符串中字符的个数时,strncpy函数将源字符串中的字符拷贝到目标空间后不够的将用’\0’填充。

举个例子:

#include<stdio.h>
#include<string.h>
int main()
{
	char arr1[10] = "#########";
	char arr2[] = "abcd";
	strncpy(arr1, arr2, 3);
	strncpy(arr1, arr2, 6);
	return 0;
}

当操作数为3时,拷贝结束后arr1数组中存放的是"abc######";而当操作数为6时,拷贝结束后arr1数组中存放的是"abcd\0\0###"。

strncmp - 限制操作长度

函数介绍

int strncmp( const char *string1, const char *string2, size_t count );

strncmp的参数与strcmp相比较也多出了一个参数,而这个参数也就是需要比较的字符个数。

举个例子:

#include<stdio.h>
#include<string.h>
int main()
{
	char arr1[] = "abcde";
	char arr2[] = "abcdf";
	int ret1 = strncmp(arr1, arr2, 4);
	int ret2 = strncmp(arr1, arr2, 5);
	return 0;
}

当操作数为4时,我们只比较了arr1和arr2的前4个字符,而它们前4个字符都相同,所以返回的是0;而当操作数为5的时候,我们比较了arr1和arr2的前5个字符,因为字符’e’的ASCII值小于字符’f’的ASCII值,所以返回一个负值。 

strncat - 限制操作长度

函数介绍

char *strncat( char *Dest, const char *Source, size_t count );

strncat的参数与strcat相比较也多出了一个参数,而这个参数也就是需要被操作的字符个数。
注意:

  • 当操作数小于源字符串中的字符个数时,操作数的大小决定被追加的字符个数,并在追加完后再追加一个’\0’。
  • 当操作数大于等于源字符串中的字符个数时,将源字符串内容全部追加到目标空间便结束追加。

举个例子:

#include<stdio.h>
#include<string.h>
int main()
{
	char arr1[10] = "abc\0#####";
	char arr2[] = "def";
	strncat(arr1, arr2, 2);
	strncat(arr1, arr2, 5);
	return 0;
}

当操作数为2时,拷贝结束后arr1数组中存放的是"abcde\0###";而当操作数为5时,拷贝结束后arr1数组中存放的是"abcdef\0##"。

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

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

相关文章

小黑周末惊心动魄进“村”赶考,周一继续拖着疲惫的身体来实习的leetcode之旅:968. 监控二叉树

小黑看完题解思路后代码 # Definition for a binary tree node. # class TreeNode: # def __init__(self, val0, leftNone, rightNone): # self.val val # self.left left # self.right right class Solution:def minCameraCover(self, root: …

前端、后台(ueditor)富文本的使用和回显

前端、后台富文本的使用前端如何配置ueditorjson文件注释一、html、js引用ueditor二、后端返回ueditor的json文件1.配置接受ueditor的接口三、回显效果图&#xff1a; 前端如何配置ueditor 下载ueditor资源文件地址&#xff1a; ueditor资源文件压缩包下载 下载解压后放在…

LLM笔记

LLM其实就是large language model&#xff0c;大语言模型。 AGI其实就是Artificial General Intelligence&#xff0c;通用人工智能。 如果对“最终任务”进一步进行分类&#xff0c;又大致可以分为两大不同类型的任务&#xff1a;自然语言理解类任务和自然语言生成类任务。如果…

YOLOv6: 面向工业应用的单阶段目标检测框架

论文地址&#xff1a;https://arxiv.org/pdf/2209.02976 代码地址&#xff1a;https://github.com/meituan/YOLOv6 多年来&#xff0c;YOLO 系列一直是高效目标检测的行业标准。 YOLO 社区蓬勃发展&#xff0c;丰富了其在众多硬件平台和丰富场景中的使用。在这份技术报告力求…

云原生Docker搭建chemex资产管理系统

这篇文章主要讲解如何使用Ubuntu系统安装Docker应用并且搭建Chemex资产管理系统 Chemex数据是存在数据库的&#xff0c;为了方便备份以及管理容器。可利用外部的数据库或者Docker搭建一个数据库出来。我这里就在Docker容器中创建一个Mysql数据库供Chemex资产管理系统使用。 一…

QQ浏览器是如何提升搜索相关性的?

导言 | 搜索相关性主要指衡量Query和Doc的匹配程度&#xff0c;是信息检索的核心基础任务之一&#xff0c;也是商业搜索引擎的体验优劣最朴素的评价维度之一。本文作者刘杰主要介绍QQ浏览器搜索相关性团队在相关性系统及算法方面的实践经历。值得一提的是&#xff0c;本文会特别…

数组常用方法总结 (2) :sort / join / reverse / concat

sort 排序后会改变原有数组。简单数组和对象数组都可以进行排序。默认升序排序。 <template><div class"myBlock"><div class"tableBlock"><div class"title">{{ newObject ? "操作后的数组" : "操作…

NEW | GOT Online支持多模式采集、Mono分析支持IL2CPP打包

在性能优化时&#xff0c;你是否也遇到过这样的困扰&#xff1a;和性能相关的参数非常多&#xff0c;为什么能保障广度&#xff0c;就没法保障深度&#xff1f;这是因为数据的获取本身存在打点消耗&#xff0c;如果获取全量数据势必存在大量打点操作&#xff0c;导致收集的数据…

定时任务、cron表达式、springBoot整合定时任务和异步任务-58

一&#xff1a;定时任务 1.1 官网地址 http://www.quartz-scheduler.org/documentation/quartz-2.3.0/tutorials/crontrigger.html 1.2 cron表达式 Cron表达式是一个字符串&#xff0c;字符串以5或6个空格隔开&#xff0c;分为6或7个域&#xff0c;每一个域代表一个含义&am…

Spring 教程

Spring 教程Spring 概述三层架构Spring 的优良特性使用 Spring 框架的好处依赖注入&#xff08;DI&#xff09;Spring 框架具有以下几个特点&#xff1a;1&#xff09;方便解耦&#xff0c;简化开发2&#xff09;方便集成各种优秀框架3&#xff09;降低 Java EE API 的使用难度…

如何集成GATEWAY作为网关(含网关404和503的解决办法)

新建model包 gateway 引入依赖 <dependencies><!--引入gateWay--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId></dependency><!--新版本cloud去掉了负…

ESP32设备驱动-SHT31温度湿度传感器驱动

SHT31温度湿度传感器驱动 文章目录 SHT31温度湿度传感器驱动1、SHT31介绍2、硬件准备3、软件准备4、驱动实现1、SHT31介绍 SHT31 是 Sensirion 的下一代温湿度传感器。 它建立在一个新的 CMOSens 传感器芯片之上,该芯片是 Sensirion 新湿度和温度平台的核心。 与其前身相比,…

[数据库迁移]-LVM逻辑卷管理

[数据库迁移]-LVM逻辑卷管理 森格 | 2023年1月 1、本文旨在记录数据库迁移过程&#xff08;下云至机房&#xff09;中&#xff0c;对新磁盘做逻辑卷管理的过程&#xff0c;并对Linux的文件系统和分区做了相关介绍&#xff0c;如有不对之处&#xff0c;敬请指正。 2、对Linux文…

【实践】百度信息流推荐系统质效合一的交付系统建设

省时查报告-专业、及时、全面的行研报告库省时查方案-专业、及时、全面的营销策划方案库【免费下载】2022年12月份热门报告盘点百度APP Feed流业务架构变迁思考和升级实践罗振宇2023年跨年演讲PPT原稿吴晓波2022年年终秀演讲PPT原稿《底层逻辑》高清配图‍基于深度学习的个性化…

数据结构:关于时间复杂度的例题计算

1、嵌套循环时间复杂度的计算 该程序&#xff0c;最上面的嵌套循环里&#xff0c;i每执行一次&#xff0c;j就执行N次&#xff0c;所以嵌套循环执行次数为N*N次&#xff1b;中间的k变量循环了2*N次&#xff1b;最后M变量循环10次。所以总共执行了 N*N2*N10 次&#xff01; 所以…

ERROR: Could not find a version that satisfies the requirement six>=1.9.0

问题分析 ERROR: Could not find a version that satisfies the requirement six>1.9.0 (from prompt-toolkit) (from versions: none) ERROR: No matching distribution found for six>1.9.0 出现这个问题的原因是python国内网络不稳定&#xff0c;用pip管理工具安装库…

websocket创建时附加额外信息 [如自定义headers信息(利用nginx)]

目录 情景描述&#xff1a; 解决方案 一、服务端要求前端创建websocket时附带的token&#xff0c;必须放在request的headers中&#xff08;常出现在第三方的合作中&#xff09;&#xff1b; 思路&#xff1a; 整体效果&#xff1a; 具体步骤&#xff1a; 二、服务端只需要获…

【Unity云消散】巩固step,lerp和smoothstep

之前在学习HLSL常用函数时就有涉及到范围相关的函数&#xff0c;但是最近做的东西发现step,lerp和smoothstep这三个函数总是一直在用&#xff0c;总是会在用的时候突然忘记他们分别是干啥的&#xff0c;这里就记录一下方便查看吧&#xff0c;形式大部分参考Unity Shader 极简实…

单绞机控制算法(线缆行业)

在了解单绞机之前需要大家对收放卷以及排线控制有一定的了解,不清楚的可以参看下面几篇博客,这里不再赘述,受水平和能力所限,文中难免出现错误和不足之处,诚恳的欢迎大家批评和指正。 收放卷行业开环闭环控制算法 PLC张力控制(开环闭环算法分析)_RXXW_Dor的博客-CSDN博…

whistle本地代理线上(vue项目)

安装whistle install -g whistle 安装好后&#xff0c;cmd控制台启动whistle “w2 start” 访问whistle本地前端 127.0.0.1:8899 &#xff08;一般是这个网址&#xff09; 这里要注意如果您当前使用的是https类型的域名&#xff0c;需要安装https证书&#xff0c;这样才能代…