【C进阶】字符串函数

news2024/11/26 15:44:08

 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;
	else
		return 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);//err
	printf("%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);//A

	ret = tolower(ret);
	printf("%c\n", ret);//a
	return 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.coloradmin.cn/o/1075692.html

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

相关文章

C语言之动态内存管理_柔性数组篇(2)

目录 柔性数组的特点 柔性数组的使用 动态内存函数增容柔性数组模拟实现 柔性数组的优势 今天接着来讲解一下柔性数组知识。 柔性数组的特点 C99中&#xff0c;结构中的最后一个元素允许是未知大小的数组&#xff0c;这就叫做【柔性数组】成员。 结构体中最后一个成员未…

PyCharm搭建Scrapy环境

Scrapy入门 1、Scrapy概述2、PyCharm搭建Scrapy环境3、Scrapy使用四部曲4、Scrapy入门案例4.1、明确目标4.2、制作爬虫4.3、存储数据4.4、运行爬虫 1、Scrapy概述 Scrapy是一个由Python语言开发的适用爬取网站数据、提取结构性数据的Web应用程序框架。主要用于数据挖掘、信息处…

k8spod就绪检查失败

pod 一直未就绪 kube-system metrics-server-7764f6c67c-2kts9 0/1 Running 0 10m kubect describe 查看 就绪探针未通过 Normal Started 3m19s kubelet Started container metrics-server Warning Unhealthy 5s (x20 over 2m55s) kubelet Readiness probe failed: HTTP probe…

Springboot整合Hutool自定义注解实现数据脱敏

一、前言 我们在项目中会处理敏感数据&#xff08;如手机号、身份证号、姓名、地址等&#xff09;时&#xff0c;通常需要对这些数据进行脱敏&#xff0c;以确保数据隐私和安全。 我们本次使用 Hutool 库来轻松实现数据脱敏&#xff0c;如果项目中不让使用&#xff0c;可以自…

各类高危漏洞介绍及验证方式教程(二)

本期整理的漏洞验证教程约包含50多类漏洞&#xff0c;分多个章节编写&#xff0c;可从以下链接获取全文&#xff1a; 各类高危漏洞验证方式.docx (访问密码: 1455) 搭建dvwa测试环境基础教程.docx(访问密码: 1455) web逻辑漏洞挖掘快速入门基础教程.docx (访问密码: 1455) 06 I…

工作杂记-YUV的dump和read

工作小记-YUV的dump和read 工作杂记-YUV的dump和read利用dump生成图片 yuv2imgyuv2img代码 工作杂记-YUV的dump和read 工作中涉及到模型验证相关的工作&#xff0c;这里是三个模型的共同作用&#xff0c;在感知模型读取图片的时候&#xff0c;把输入替换成自己给定的输入&…

Python中如何快速解析JSON对象数组

嗨喽~大家好呀&#xff0c;这里是魔王呐 ❤ ~! python更多源码/资料/解答/教程等 点击此处跳转文末名片免费获取 由于浏览器可以迅速地解析JSON对象&#xff0c;它们有助于在客户端和服务器之间传输数据。 本文将描述如何使用Python的JSON模块来传输和接收JSON数据。 JavaSc…

优思学院|揭秘六西格玛:七大迷思你不可不知!

六西格玛的核心理念起源于1970年在摩托罗拉公司诞生。其基本精神一直是持续改进和提升品质&#xff0c;随后在各国呈爆炸性的发展。自2000年开始引进中国后&#xff0c;已经过了约16年的应用。但以2017年的角度回顾中国整体六西格玛的应用广度及熟悉度&#xff0c;发现六西格玛…

【ftp篇】 vsftp(ftp) 每天生成一个动态密码

这里写目录标题 前言为什么需要动态每日生成一个密码&#xff1f;编写脚本定时任务java对应的代码 前言 社长最近接到一个需求&#xff0c;需要ftp每天动态生成一个密码 为什么需要动态每日生成一个密码&#xff1f; 在软硬件通讯过程中&#xff0c;就以共享单车为例&#xff0…

Java解析E文件工具类

import lombok.extern.slf4j.Slf4j;import java.io.*; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.List;/*** Description E文件工具类*/ Slf4j public class EFileUtils {/*** 读字符串* param text …

智慧公厕的原理与优势,了解一种更智能的卫生设施

智慧公厕是一种基于现代科技的智能化卫生设施&#xff0c;它的出现给人们的生活带来了巨大的改变和便利。本文将详细介绍智慧公厕的原理和优势&#xff0c;让我们一起了解一种更智能的卫生设施。 智慧公厕的原理主要基于物联网技术。通过将公厕内部各种设备和设施连接到互联网…

Java使用Hutool工具包将汉字转换成汉语拼音

主题&#xff1a;使用Java将汉字转换成拼音 介绍 在Java开发中&#xff0c;有时候我们需要将汉字转换成拼音&#xff0c;以方便进行数据处理、搜索和排序等操作。本文将介绍如何使用Hutool和Pinyin4j这两个Java库来实现汉字转拼音的功能。 依赖库介绍 在开始之前&#xff0c;…

无人直播矩阵系统源码开发------

全自动无人直播系统是一款让商家和企业实现无人直播的系统软件&#xff0c;让商家在门店直播卖货&#xff0c;实现解放双手&#xff0c;无需过多的人工干预。为了满足不同用户的需求&#xff0c;我们推出了OEM功能&#xff0c;让用户可以轻松地将该系统集成到自己的应用程序中。…

软考高项-第九章:项目范围管理

重要知识点&#xff1a; 以上总结&#xff0c;仅供参考。

视频通话中的Camera操作

视频通话也有打开本地摄像头预览的场景&#xff0c;但打开本地Camera预览逻辑&#xff0c;并非在Dailer APP中实现&#xff0c;具体流程图如下。 Dialer app中只调用 1、setCamera用于打开摄像头 相关动作在Ims apk中实现&#xff0c;open函数最后调用了VTSource.java中的doOp…

Python+Pickle/Parquet/HDF5...不同文件格式存储模式下的量化因子计算性能对比

在量化交易中&#xff0c;基于金融市场 L1/L2 报价和交易高频数据进行高频因子计算是一项常见的投研需求。随着金融市场数据量的不断增加&#xff0c;传统的关系数据库已经难以满足大规模数据的存储和查询需求。为了应对这一挑战&#xff0c;一部分用户选择了分布式文件系统&am…

2023年中国在线语言教育行业发展趋势分析:预计2026年在线语言教育市场规模有望实现1182.3亿元[图]

在线语言教育是指在信息化时代的背景下&#xff0c;使用电脑、手机、平板等基于网络的工具在任何地方、任何时间都可以对语言进行学习的一种教育方式&#xff0c;包括校内在线语言教育和校外在线语言教育。在线语言教育是对未来教育模式的探索&#xff0c;以数字化为杠杆&#…

03_学习springdoc与微服务结合_简述

文章目录 1 前言2 基本结构3 网关的配置3.1 ✍️ pom.xml 引入依赖3.2 &#x1f33f; application.yml 的配置3.2.1 Gateway 的配置3.2.2 Eureka Client 的配置3.2.3 Springdoc 的配置 3.3 Springdoc 配置类 4 影片服务 backend-film 的配置4.1 ✍️ pom.xml 引入依赖4.2 &…

Go语句与表达式深度解析:全案例手册

目录 语句1. 声明语句1.1 变量声明1.2 常量声明 2. 赋值语句3. 控制流语句3.1 条件语句if语句switch语句 3.2 循环语句for语句 3.3 跳转语句break语句continue语句return语句goto语句 4. 其他语句4.1 defer语句4.2 go语句 实战案例 表达式介绍、详解、举例1. 基础表达式1.1 字面…

在线免费无时长限制录屏工具 - 录猎在线版

需要录屏的小伙伴注意啦&#xff0c;想要长时间录制又不想花钱的&#xff0c;可以看下这款在线版录屏软件 —— 录猎在线版&#xff0c;一个录屏软件所需要的基本功能它都有&#xff0c;设置录制范围、录制的声音来源、摄像头也能录制的。同时它是支持Windows和Mac系统的&#…