【C语言进阶(6)】字符串函数的使用方法 + 模拟实现

news2025/1/18 20:11:09

文章目录

  • Ⅰ 字符操作函数
    • ⒈字符分类函数
    • ⒉字符转换函数
  • Ⅱ 字符串操作函数
    • ⒈strlen
    • ⒉strcpy
    • ⒊strcat
    • ⒋strcmp
    • ⒌strncpy
    • ⒍strncat
    • ⒎strncmp
    • ⒏strstr
    • ⒐strtok
    • ⒑strerror
  • Ⅲ 模拟实现字符串函数
    • ⒈模拟实现 strlen
    • ⒉模拟实现 strcpy
    • ⒊模拟实现 strcat
    • ⒋模拟实现 strcmp
    • ⒌模拟实现 strncpy
    • ⒍模拟实现 strncat
    • ⒎模拟实现 strncmp
    • ⒏模拟实现 strstr

本文重点介绍函数

  • 求字符串长度 —— strlen
  • 长度不受限制的字符串函数 —— strcpy、strcat、strcmp
  • 长度受限制的字符串函数 —— strncpy、strncat、strncmp
  • 字符串查找函数 —— strstr、strtok
  • 错误信息报告函数 —— strerror

Ⅰ 字符操作函数

引用头文件

  • <ctype.h>

⒈字符分类函数

函数名函数功能
iscntrl判断是否是控制字符
isspace判断是否是空白字符:空格‘ ’,换页‘\f’,换行’\n’,回车‘\r’,制表符’\t’或者垂直制表符’\v’
sdigit判断是否是十进制数字: 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判断是否是任何可打印字符,包括图形字符和空白字符

⒉字符转换函数

函数名函数功能
toupper将小写字母转换成大写字母
tolower将大写字母转换成小写字母

函数用例

int main()
{
	char str[] = "i WANT TO PLAY bLACK mYTH wUKONG!";

	for (int i = 0; i < strlen(str); i++)
	{
		if (islower(str[i]))
		{
			str[i] = toupper(str[i]);
		}
		else if (isupper(str[i]))
		{
			str[i] = tolower(str[i]);
		}
	}

	printf("%s\n", str);

	return 0;
}

在这里插入图片描述

Ⅱ 字符串操作函数

字符串函数相关知识

  • 字符串末尾一定悄悄的藏着一个 ’ \0 '。
  • 字符串函数引用头文件:<string.h>
  • 只能对字符串进行操作,有 ’ \0 ’ 这个限制条件

⒈strlen

求字符串长度

size_t strlen ( const char * str );
  • size_t 是个无符号整形,想打印 strlen 函数的返回值最好使用 %zd 。

函数功能

  • 求字符串长度,返回 ’ \0 ’ 之前出现的所有字符个数。

函数参数

  • 一个字符串的首字符的地址。

函数用例

在这里插入图片描述

⒉strcpy

拷贝字符串

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

函数功能

  • 将 source 指向的字符串(包含 ’ \0 ')复制到 destination 指向的数组中,然后返回 destination 空间的起始地址。
  • 为了避免溢出,destination 指向的数组的大小应该 足够容纳 source 指向的串中得所有内容。

在这里插入图片描述

函数参数

  • source:指向被复制的字符串。
  • destination:指向要在其中复制内容的目标数组。

返回值

  • destination 数组的起始地址。

函数用例

在这里插入图片描述

⒊strcat

连接字符串

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

函数功能

  • 将一个字符串(包含 ’ \0 ')连接到另一个字符串的后面。
  • 要求目标数组里已经包含一个字符串(可以是空串),找到目标字符串的末尾并将源字符串 cv 过去。

函数参数

  • source:要追加的字符串,与目的地的内存不能重叠。
  • destination:指向目标数组的指针,该数组应包含一个字符串,并且足够大以包含连接的结果字符串。

返回值

  • destination 数组的起始地址。

函数用例

在这里插入图片描述

⒋strcmp

比较字符串

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

函数功能

  • 比较两个字符串的是否相同
  • 从第一个字符开始,依次对比两个字符串中每个对应字符的 ASCII 码值的大小。如果第一个字符串的字符的 ASCII 码小于第二个字符串对应字符的 ASCII 码,则返回一个小于 0 的值;如果大于,则返回一个大于 0 的值。

在这里插入图片描述

函数参数

  • str1:指向第一个字符串的起始地址。
  • str2:指向第二个字符串的起始地址。

函数用例

在这里插入图片描述

⒌strncpy

指定拷贝字符数

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

函数功能

  • 在实现程序复制的时候,应该限制源字符串的长度,确保目标数组在执行完复制后不会发生溢出。
  • 于是就有了 strncpy。在 strcpy 的基础上添加了一个参数,用来指定拷贝的字符个数

函数参数

  • source:指向被复制的字符串。
  • destination:指向要在其中复制内容的目标数组。
  • num:指定从 source 指向的字符串中拷贝 num 个字符到 destination 指向的目标数组中。

函数用例

在这里插入图片描述

⒍strncat

指定连接字符数

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

函数功能

  • 指定从 source 指向的串中连接 num 个字符到 destination 指向的字符串的末尾
  • 与 strncpy 不同的是,strncat 总是在连接后自动追加一个结束符(’ \0 ')

函数用例

在这里插入图片描述

⒎strncmp

指定比较字符数

int strncmp ( const char * str1, const char * str2, size_t num );

函数功能

  • 指定比较两个字符串的前 num 个字符

函数用例

在这里插入图片描述

⒏strstr

找字符串子串

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

函数功能

  • 在 str1 指向的字符串中查找是否存在字符串 str2
  • 看看 str1 里面是否包含 str2,如果包含则返回首次出现 str2 字符的起始地址,反之则扔一个空指针回来。

在这里插入图片描述

函数参数

  • str1:指向待查找的字符串。
  • str2:指向被查找的字符串。

函数用例

在这里插入图片描述

⒐strtok

分割字符串

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

函数功能

  • 按照 delimiters 中提供的分割符,将 str 中出现提供的分割符的位置改成 \0,并将 str 这个串在此处结尾。
char str[] = "wwwoBlack_myth_wukong0com";
char* deli = "o_0";

- 按照 deli 提供的字符对 str 进行分割;
- 将 str 中 o_0 这几个字符的位置换成 \0,www\0Black_myth_wukong0com
- 然后返回分隔的位置之前的串的首地址

函数参数

  • str:指向待被分割的字符串。
  • delimiters:用作分割符的字符(可以是一个字符,也可以是一个集合)。

返回值

  • 返回被分解的第一个子字符串的首字符地址。
  • 若无可检索的字符串,则返回 NULL1。

注意事项

  • strtok 函数会改变被操作的字符串,所以在使用 strtok 函数切分的字符串一般都是临时拷贝的内容并且可修改。
  • strtok 的第一个参数不为 NULL 时,函数将找到 str 中第一个分割符,strtok 函数将保存它在字符串中的位置。

在这里插入图片描述

  • strtok 的第一个参数是为 NULL 时,函数将在同一个字符串中被保存的位置开始,查找下一个分割符。

在这里插入图片描述

  • 如果 strtok 函数找不到分割符,则返回 NULL。

函数用例

  • strtok 函数会记录 str 串中出现分割符的位置,不用担心传 NULL 会找不到 str。

在这里插入图片描述

⒑strerror

错误报告函数

char * strerror ( int errnum );

函数功能

  • 返回错误码所对应的错误信息
  • 错误码对应的错误信息不需要去记,C 语言的库函数在执行失败的时候,都会设置错误码。

函数用例

在这里插入图片描述

实际使用

  • 如果在可能发生错误的情况下需要先判断错误信息,这个时候时没有提供错误码的。

  • 此时就需要将错误码先记录在 errno 变量里,直接将 errno 交给 strerror 即可,就不用每次手动输入错误码那么挫了。

  • errno:C 语言设置的一个存放错误码的全局变量。引用头文件:<errno.h>。当出现新的错误码时,会将旧的覆盖掉,始终记录最新的。

在这里插入图片描述

Ⅲ 模拟实现字符串函数

  • 模拟实现字符串函数,就得保证自定义函数的(参数、返回值)同库函数一致

⒈模拟实现 strlen

1. 计数器法

size_t my_strlen(const char* str)
{
	assert(str);
	size_t len = 0;
	
	while (*str++)
	{
		len++;
	}

	return len;
}

2. 递归法

size_t my_strlen(const char* str)
{
	assert(str);

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

3. 指针 - 指针法

size_t my_strlen(const char* str)
{
	assert(str);
	char* start = str;
	char* end = start;

	while (*++end != '\0');

	return end - start;
}

⒉模拟实现 strcpy

char* my_strcpy(char* dest, const char* sour)
{
	assert(dest && sour);
	char* start = dest;;		//源头的地址放到目标空间中会导致目标空间的地址被改变,先用 start 保留目标空间的地址

	while (*dest++ = *sour++);	//将 sour 指向的包含 '\0' 的字符串赋给 dest 指向的空间

	return start;				//返回目标空间的起始地址
}

⒊模拟实现 strcat

char* my_strcat(char* dest, const char* sour)
{
	assert(dest && sour);
	char* start = dest;			//记录目标空间的起始地址

	while (*++dest);			//找到目标空间的 '\0' 就停止
	while (*dest++ = *sour++);	//将 sour 指向的串连接过去

	return start;				//返回目标空间的起始地址
}

⒋模拟实现 strcmp

int my_strcmp(const char* str1, const char* str2)
{
	assert(str1 && str2);

	while (*str1 == *str2)	//对应字符 ASCII 相等才会往后找不同
	{
		if ('\0' == *str1)	//在对应字符相等的情况下,*st1 = '\0',说明 *str1 和 *str2 都等于 '\0'
		{
			return 0;		//此时两个字符串完全相等
		}

		str1++;
		str2++;
	}

	return *str1 - *str2;	//返回对应字符 ASCII 码的差值
}

代码优化

int my_strcmp(const char* str1, const char* str2)
{
	assert(str1 && str2);

	while (*str1++ == *str2++);			//循环结束后 str1 和 str2 分别指向两个不想等字符的后一位

	return *(str1 - 1) - *(str2 - 1);	//重新指向两个不相等的字符然后解引用,用 ASCII 码值做差
}

⒌模拟实现 strncpy

char* my_strncpy(char* dest, const char* sour, size_t num)
{
	assert(dest && sour);

	int i = 0;
	char* start = dest;

	for (i = 0; i < num; i++)
	{
		*dest++ = *sour++;
	}
	
	return start;
}

⒍模拟实现 strncat

char* my_strncat(char* dest, const char* sour, size_t num)
{
	assert(dest && sour);
	char* start = dest;

	while (*++dest);				//找到串 1 的 '\0' 的位置

	for (int i = 0; i < num; i++)
	{
		if (!(*dest++ = *sour++))	//两个串任何一个遇到 '\0' 都直接结束函数调用
		{
			return start;
		}
	}
	*dest = '\0';					//在末尾补上一个 '\0'

	return start;
}

⒎模拟实现 strncmp

int my_strncmp(const char* str1, const char* str2, size_t num)
{
	assert(str1 && str2);

	while (*str1++ == *str2++ && --num);	//比较到第 num 个字符循环 num - 1 次即可

	return *(str1 - 1) - *(str2 - 1);		//让两个指针重新指向不相等字符的位置
}

⒏模拟实现 strstr

解题思路

在这里插入图片描述
在这里插入图片描述

结束条件

  • s2 -> ‘\0’:当 s2 指向 \0 时,说明 str2 的全部连续的内容已经在 str1 中找到了,str2 是 str1 的子串,返回 p(str1 首次出现 str2 的地址)即可。
  • p -> ‘\0’:当 p 指向 \0 时说明直到走到 str1 的尽头,s2 都没走到 \0,str1 中没有包含 str2 的子串,返回一个空指针即可。

代码实现

const char* my_strstr(const char* str1, const char* str2)
{
	assert(str1 && str2);

	const char* s1 = str1;					//记录str1位置
	const char* s2 = str2;					//记录str2位置
	const char*  p = str1;					//记录起始位置

	while (*p)								//*p 指向 \0 时说明主串已经走到头了
	{
		s1 = p;								//字符不相等时让 s1 指向 p 的位置
		s2 = str2;							//字符不相等时让 s2 回到 str2 的初始位置

		while (*s1 && *s2 && *s1 == *s2)	//字符相等且 s1,s2 都未指向 \0 时比较下一对字符
		{
			s1++;
			s2++;
		}

		if ('\0' == *s2)					//s2 走到 \0 时说明 str2 是 str1 的子串
		{
			return p;						//返回在 str1 中首次出现 str2 的地址
		}

		p++;								//不相等时让 s1 从 p 的下一个位置开始比较
	}

	return NULL;							//都走到这步了说明 str2 肯定不是 str1 的子串
}

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

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

相关文章

ABB DI651电源模块

数字输入&#xff1a; DI651模块是用于读取数字输入信号的设备。它可以接收来自传感器、开关、按钮等外部设备的数字信号。 信号类型&#xff1a; 这种模块通常可以处理不同类型的数字信号&#xff0c;例如开关信号、脉冲信号、状态信号等。 通道数量&#xff1a; DI651模块可…

浪涌保护器的工作原理和应用

浪涌保护器是一种用于限制瞬态过电压和泄放浪涌电流的装置&#xff0c;主要用于保护低压电力系统和电子设备免受雷击或其他因素引起的电压冲击的损害。浪涌保护器的工作原理是利用其内部的非线性元件&#xff0c;在正常状态下呈现高阻抗&#xff0c;不影响电路的正常运行&#…

企业财务管理,重点指标分析

一、偿债能力指标 偿债能力指标是一个企业财务管理的重要管理指标&#xff0c;是指企业偿还到期债务&#xff08;包括本息&#xff09;的能力。偿债能力指标包括短期偿债能力指标和长期偿债能力指标&#xff0c;衡量短期偿债能力的指标主要有流动比率、速动比率和现金流动负债…

PlantUML文本绘制类图

记录下文本绘制类图的语法 参考 https://juejin.cn/post/6844903731293585421 类的UML表示 使用UML表示一个类&#xff0c;主要由三部分组成。类名、属性、方法。其中属性和方法的访问修饰符用 - 、# 、 表示 private、protected、public。 如图所示&#xff0c;表示A类有一个…

1007 Maximum Subsequence Sum

Given a sequence of K integers { N1​, N2​, ..., NK​ }. A continuous subsequence is defined to be { Ni​, Ni1​, ..., Nj​ } where 1≤i≤j≤K. The Maximum Subsequence is the continuous subsequence which has the largest sum of its elements. For example, g…

电商数据采集和分析

什么是电商数据采集&#xff1f; 数据采集一般是指利用人工或者爬虫技术&#xff0c;对电商平台上的公开数据进行抓取采集&#xff0c;采集完成后&#xff0c;去除客户不需要的无关的杂质数据&#xff0c;再进行交付。 电商数据采集要注意哪些点&#xff1f; 首先是采集平台&…

vue 简单实验 自定义组件 局部注册

1.概要 2.代码 <html> </html> <script src"https://unpkg.com/vuenext" rel"external nofollow" ></script> <body><div id"counter"><component-a></component-a></div> </body&g…

【方案】安防监控EasyCVR智慧工地视频监管风险预警平台的应用

智慧工地方案是一种结合现代化技术与工地管理实践的创新型解决方案。它通过实时监控、数据分析、人工智能等技术手段&#xff0c;使工地管理更加高效、智能化。在建设智慧工地的过程中&#xff0c;除了上述提到的利用物联网技术实现设备互联、数据采集及分析以外&#xff0c;还…

快捷支付和网上支付、协议支付都有哪些区别?

协议支付是指客户通过与银行签订协议&#xff0c;将客户的银行账户连接到商户的相关账户&#xff0c;并在支付时直接输入相关账户的支付密码。通过与特定商户共同为客户提供的电子支付方式&#xff0c;客户在中国银行开立的银行账户与客户在特定商户的用户ID绑定&#xff0c;并…

华为“鸿鹄之志”:2025年L4,2030 年实现 L5,全场景AI指日可待

在第七届未来网络发展大会上&#xff0c;华为公司的彭松发表了题为《打造端到端 AI 网络&#xff0c;打通全场景 AI 能力》的主题演讲。他指出&#xff0c;AI 时代网络创新包括 Network for AI 和 AI for Network 两个方面的目标。 前者旨在构建支持AI业务的网络&#xff0c;实…

冠达管理:股票基本知识有哪些?建议新手了解!

股票教育是存在较大危险的&#xff0c;所以新手出资者在进行股票买卖之前主张先了解股票基本常识。那么股票基本常识有哪些&#xff1f;主张新手了解!下面就为大家剖析&#xff1a; 1、股票含义 股票是股份有限公司所有权的一部分&#xff0c;也是发行的所有权凭据&#xff0c…

HTTP协议初识·上篇

目录 认识URL urlencode和urldecode 如何编码解码和验证过程 一个基本的网络服务器的流程 代码验证请求与响应 准备工作 HTTPServer.hpp Protocol.hpp makefile 1请求 HTTPServer.hpp 1.0函数handlerHttp-基本流程 再次处理 HttpServer.cc(新建文件) 测试1 -- 请…

JS的事前循环Event Loop

前言 javascript是单线程脚本语言&#xff0c;并非指js只有一个线程&#xff0c;而是同一时刻只能有一个线程在工作。 js异步如何实现 如果 JS 中不存在异步&#xff0c;只能自上而下执行&#xff0c;如果上一行解析时间很长&#xff0c;那么下面的代码就会被阻塞。 对于用户…

【直接收藏】前端 VUE 高阶面试题(三)

86.说说vue生命周期&#xff0c;发送请求在生命周期的哪个阶段&#xff0c;为什么不可以是beforeMount&#xff0c;mounted中 回答&#xff1a; 1、vue的生命周期 1&#xff09;、生命周期是什么&#xff1f; Vue 实例有一个完整的生命周期&#xff0c;也就是从开始创建、初始…

C++信息学奥赛1148:连续出现的字符

代码题解&#xff1a; #include <iostream> #include <string> using namespace std; int main() {int n;// 输入一个整数ncin>>n;cin.ignore();string str1;// 输入一行字符串getline(cin,str1);for(int i0;i<str1.length();i){int a0;for(int ji;j<…

自定义拖拽功能,上下拖拽改变盒子高度

核心在于监听鼠标的move来改变div的高度&#xff0c;抽成了组件 <template><div ref"container" class"drag"><z-tooltip v-if"isShowIcon" effect"dark" content"格式化" placement"top-start"&…

Windows11 安装 nvm node版本管理工具

在 Windows 11 上安装并配置 NVM 与 Node.js 版本管理工具 引言&#xff1a; Node.js 是一款强大的开发工具&#xff0c;而版本管理工具 NVM 则可以帮助我们在不同的项目中灵活地切换和管理 Node.js 版本。本篇博客将为大家介绍如何在 Windows 11 操作系统上安装 NVM&#xff…

【Go Web 篇】从零开始:构建最简单的 Go 语言 Web 服务器

随着互联网的迅速发展&#xff0c;Web 服务器成为了连接世界的关键组件之一。而在现代编程语言中&#xff0c;Go 语言因其卓越的性能和并发能力而备受青睐。本篇博客将带你从零开始&#xff0c;一步步构建最简单的 Go 语言 Web 服务器&#xff0c;让你对 Go 语言的 Web 开发能力…

什么是确认测试报告?确认测试报告的用途和周期?

确认测试又称有效性测试&#xff0c;其任务是验证软件的功能和性能及其他特性是否与用户的要求一致。确认测试需要提供的资料包括&#xff1a; 软件需求规格说明书&#xff1a;列出了软件的功能和性能要求&#xff0c;是确认测试的依据。 确认测试计划&#xff1a;制定了确认…

JUC集合、map线程安全

文章目录 在并发场景下&#xff0c;集合产生的问题解决方案VectorsynchronizedListCopyOnWriteArrayList写时赋值技术什么是写实复制技术&#xff1a; HashSet线程不安全问题解决办法 HashMap线程不安全总结特点 在并发场景下&#xff0c;集合产生的问题 现在下面这段代码&…