C语言笔记第9篇:字符函数和字符串函数

在编程的过程中,我们经常要处理字符和字符串,为了方便操作字符和字符串,C语言标准库中提供了一系列库函数,接下来我们就学习一下这些函数。

一、字符函数

1、字符分类函数

C语言中有一系列的函数是专门做字符分类的,也就是一个字符是属于什么类型的字符的。这些函数的使用都需要包含一个头文件是ctype.h

函数如果他的参数符合下列条件就返回真
iscntrl任何控制字符
isspace空白字符:空格、换页、换行、回车、制表符
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任何可打印字符,包括图形字符和空白字符

给一段代码让大家清楚的看到字符分类函数的用法和返回值,以islower为例:

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

打印结果:

islower是分类小写字母的函数,当我们给它传一个小写字母时返回非0的值,传一个大写字母判断不是小写字母则返回0。

其他的函数和此函数的使用形式是一模一样的,可以根据以上例子进行使用。

代码练习:将字符串中的小写字母转大写,其他字符不变
#include <ctype.h>
#include <string.h>
#include <stdio.h>
int main()
{
	char str[] = "i Like Beijing!";
	size_t len = strlen(str);
	size_t i = 0;
	for (i = 0; i < len; i++)
	{
		if (islower(str[i]))//遍历判断是否小写
		{
			str[i] -= 32;//转换大写
		}
	}
	printf("%s\n", str);
	return 0;
}

2、字符转换函数

C语言提供了2个字符转换函数:

int tolower(int c);//将参数传进去的大写字母转小写
int toupper(int c);//将参数传进去的小写字母转大写

有了这个函数我们就可以将上面的代码更新一下,不需要+、-32来改变大小写字母,直接使用转换字符函数即可:

#include <ctype.h>
#include <string.h>
#include <stdio.h>
int main()
{
	char str[] = "i Like Beijing!";
	size_t len = strlen(str);
	size_t i = 0;
	for (i = 0; i < len; i++)
	{
            //不用再额外判断字母是不是小写,tolower判断该字符是小写就转换大写,不是就不改
			str[i] = toupper(str[i]);//如果是小写字母就转换为大写
            //前提是转换结果需要用一块整型空间来接收,因为传值调用,并不能在函数直接内部修改
	}
	printf("%s\n", str);
	return 0;
}

二、字符串函数

3、strlen的使用和模拟实现

strlen库函数功能:求字符串长度,统计的是结束标志\0之前出现的字符个数

strlen库函数的声明:

size_t strlen(const char* str);

strlen函数的调用:

#include <stdio.h>
#include <string.h>
int main()
{
	char str[] = "abcdef";
	size_t len = strlen(str);//调用strlen函数,返回值用size_t的变量来接收
	printf("%zd\n", len);//打印size_t类型的返回值(字符串长度)
	return 0;
}

strlen注意事项:

1、strlen所计算的字符串结尾必须有结束标志 ' \0 '。

2、必须给strlen传递字符串地址,strlen需要通过地址向后访问找到 ' \0 ' 为止,只传递字符会报错。

strlen函数的模拟实现:

仿照strlen的函数参数,返回类型,功能写一个类似的函数

方法1:遍历判断

#include <stdio.h>
#include <assert.h>
size_t my_strlen(const char* str)
{
    assert(str != NULL);
	size_t count = 0;//计数器,统计字符串的长度
	while (*str++ != '\0')//后置++,先使用,当解引用以后str++指向下一个元素
	{
		count++;//不是'\0'就让计数器++一次
	}
	return count;
}
int main()
{
	char str[] = "hello world";
	size_t len = my_strlen(str);
	printf("%zd\n", len);
	return 0;
}

方法2:指针 - 指针

#include <stdio.h>
#include <assert.h>
size_t my_strlen(const char* str)
{
    assert(str != NULL);
	char* start = str;
	while (*start != '\0')
	{
		start++;
	}
	return start - str;//\0的地址 - 首字符的地址得到地址直接的元素个数
}
int main()
{
	char str[] = "hello world";
	size_t len = my_strlen(str);
	printf("%zd\n", len);
	return 0;
}

方法3:函数递归

#include <stdio.h>
#incldue <assert.h>
size_t my_strlen(const char* str)
{
    assert(str != NULL);
	if (*str != '\0')
		return 1 + my_strlen(str + 1);
	else
		return 0;
}
int main()
{
	char str[] = "hello world";
	size_t len = my_strlen(str);
	printf("%zd\n", len);
	return 0;
}

4、strcpy的使用和模拟实现

strcpy库函数功能:将一个字符串拷贝另一个数组

strcpy函数的声明:

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

strcpy函数的调用:

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

strcpy注意事项:

1、strcpy里源字符串必须包含 ' \0 ',因为 ' \0 ' 也会被拷贝到目标空间。

2、strcpy里目标空间必须要有足够大的空间来存储这个拷贝过来的数据。

strcpy函数的模拟实现:

仿照strcpy的函数参数,功能写一个类似的函数

#include <stdio.h>
char* my_strcpy(char* dest, const char* src)
{
    char* ret = dest;
	assert(dest && src);
	while (*dest++ = *src++)//边判断,边赋值
	{
		;
	}
    return ret;
}
int main()
{
	char arr1[] = "hello world";
	char arr2[20] = { 0 };
	my_strcpy(arr2, arr1);
	printf("%s\n", arr2);
	return 0;
}

5、strcat的使用和模拟实现

strcat库函数功能:字符串追加,就是在目标空间的末尾追加上一串源字符串

strcat函数的声明:

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

strcat函数的调用:

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

从arr1末尾的 ' \0 ' 开始,拷贝源字符串arr2,将arr1的末尾追加上arr2

strcat注意事项:

1、目标空间必须有足够大的空间进行追加。

2、目标空间结尾和源字符串结尾都必须有 ' \0 '。

strcat函数的模拟实现:

仿照strcat的函数参数,功能写一个类似的函数

#include <stdio.h>
char* my_strcat(char* dest, const char* src)
{
	assert(dest && src);
	char* ret = dest;
	while (*dest != '\0')//先找到目标空间的'\0',方便追加
	{
		dest++;
	}
	while (*dest++ = *src++)//从dest的'\0'位置开始追加
	{
		;
	}
	return ret;
}
int main()
{
	char arr1[20] = "hello ";
	char arr2[] = "world";
	my_strcat(arr1, arr2);
	printf("%s\n", arr1);
	return 0;
}

6、strcmp的使用和模拟实现

strcmp库函数功能:用来比较两个字符串的大小关系

strcmp的函数声明:

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

注意strcmp比较的不是两个字符串的长度的,而是比较两个字符串中对应位置上的字符,按照字典序比较。

标准规定:

  • 第一个字符串大于第二个字符串,则返回大于0的数字
  • 第一个字符串等于第二个字符串,则返回0
  • 第一个字符串小于第二个字符串,则返回小于0的数字
  • 那么如何判断两个字符串?比较两个字符串中对应位置字符的ASCII码值的大小

strcmp函数的调用:

#include <stdio.h>
#include <string.h>
int main()
{
	int ret1 = strcmp("abcdef", "abq");
	int ret2 = strcmp("abcdef", "abcdef");
	int ret3 = strcmp("abq", "abcdef");
	printf("%d %d %d", ret1, ret2, ret3);//打印-1 0 1
	return 0;
}

strcmp函数的模拟实现:

#include <stdio.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;
}
int main()
{
	int ret = my_strcmp("abcdef", "abc");
	printf("%d", ret);
	return 0;
}

7、桃园三结义:长度受限制函数strncpystrncatstrncmp

前面的三个函数strcpy、strcat、strcmp是长度不受限制的字符串函数,他们仨还有长度受限制的函数,分别是:strncpystrncatstrncmp,和前面的strcpy、strcat、strcmp的功能是相同的,参数上多了一个值,这个值就是限制字符串函数的执行功能长度限制,表面上不同的是str后面多了一个n,干了这碗wine ( 酒 ) 我们仨就正式结拜为兄弟。

比如我要拷贝"hello world"到一个空间,但是只想拷贝 "hello" 这6个字符,就可以考虑用长度受限制的字符串拷贝函数strncpy。

strncpy函数的声明:

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

strncat函数的声明:

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

strncmp函数的声明:

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

它们的功能大概就是

strncpy限制拷贝字符个数

strncat限制字符追加个数

strncmp限制字符串比较字符个数

所以具体函数调用就不再一一介绍了,知道是什么功能限制什么就可以了

8、strstr的使用和模拟实现

strstr库函数功能:在一个字符串中查找另一个字符串,简单概述就是判断第二个字符串是不是第一个字符串的子字符串

strstr的函数声明:

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

strstr函数返回str2在str1中第一次出现的位置

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

strstr函数的调用:

#include <stdio.h>
#include <string.h>
int main()
{
	char str1[] = "abbcde";
	char str2[] = "bc";
	char* s = strstr(str1, str2);
	if (s == NULL)
	{
		printf("str2不是str1的子串\n");
	}
	else
	{
		printf("%s\n", s);
	}
	return 0;
}

strstr函数的模拟实现:

仿照strlen的函数参数,返回类型,功能写一个类似的函数

方法1:暴力求解

#include <stdio.h>
#include <string.h>
#include <assert.h>
const char* my_strstr(const char* str1, const char* str2)
{
	assert(str1 && str2);
	const char* s1 = NULL;
	const char* s2 = NULL;
	const char* start = s1;
    if(*str2=='\0')
       return str1;
	while (*start)
	{
        s1 = start;//重置位置
		s2 = str2;//重置位置
		while (*s1==*s2 && *s1!='\0')//如果相等就++继续判断下一个
		{
			s1++;
			s2++;
		}
		if (*s2 == '\0')//结束循环后判断是不是因为*s2为\0结束的
		{
			return start;//返回刚开始的判断位置
		}
		start++;//下一个位置继续对比判断
	}
	return NULL;
}
int main()
{
	char str1[] = "abbbcde";
	char str2[] = "bbc";
	const char* s = my_strstr(str1, str2);
	if (s == NULL)
	{
		printf("str2不是str1的子串\n");
	}
	else
	{
		printf("%s\n", s);
	}
	return 0;
}

方法2:KMP算法-效率高,但是难度大,难以理解

有兴趣可以自己去了解一下。

9、strtok的使用

以后学习计算机网络时,会学到点分十进制表示的IP地址,例如:192.168.101.25,由点分开的十进制就叫点分十进制,IP地址本质是一个整数,不好记,所以才有了点分十进制表示方法

既然IP地址是用 ' . ' 隔开的,那可以将每个隔开的段拿出来吗?

比如:192,168,101,25这四个由 ' . ' 隔开的段。 

当然可以。这里就要是用到strtok函数。该函数可以通过分隔符将一个字符串的每个分割段拿出来。

strtok函数的声明:

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

strtok函数功能:

  • sep参数指向一个字符串,定义了用作分隔符的字符集合
  • 第一个参数指定一个字符串,它包含了0个或者多个有sep字符串中一个或多个分隔符分割的标记
  • strtok函数找到str中的下一个标记,并将其用 ' \0 ' 结尾,返回指向这个标记的指针。(注:strtok分割字符串时是会改变传参过来的字符串的,如果不想改变就拷贝一个传参)
  • strtok函数的第一个参数不为NULL,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置
  • strtok函数的第一个参数为NULL,函数将在同一个字符串中被保存的位置开始,查找下一个标记
  • 如果字符串中不存在更多的标记,则返回NULL指针

简单来说就是第一次调用strtok函数时需要传参传一个需要分割的字符串,他会分隔符位置置为 '\0' 返回已经分割的第一段。但是它一直停留在 ' \0 '的位置,所以下一次调用直接传递NULL就可以继续沿着' \0 '的位置继续向后找分隔符分割成段并返回,直到没有可以分割的段时返回NULL。

注:如果第一次分割字符串后,想继续分割该字符串,调用时可以直接传NULL,因为出了函数不会销毁这个分割后的字符串,一直保存着这个字符串,下一次调用时可以直接传NULL便可继续使用该字符串,是因为该字符串可能被static修饰过,出了作用域不会被销毁。

如果想分割其他字符串,不想分割该字符串。就传其他字符串,不再传递NULL。然后strtok会以刚传的其他字符串为开头,下一次调用传NULL便可继续分割其他的字符串。

strtok函数的调用

#include <stdio.h>
#include <string.h>
int main()
{
	char arr[] = "linlu1024@yeah.net";
	char buf[30] = { 0 };
	strcpy(buf, arr);
	char* p = "@.";
	char* s = NULL;
	for (s = strtok(buf, p); s != NULL; s = strtok(NULL, p))//根据strtok的特性去遍历
	{
		printf("%s\n", s);//遍历打印字符串的分段
	}
	return 0;
}

10、strerror的使用

strerror库函数功能:返回一个错误信息字符串的起始地址,简单概述就是返回一个错误码所对应的错误信息字符串的起始地址,这个错误码就是我们调用时传递的实参。

strerror函数的声明:

char* strerror(int errnum);

strerror函数的调用:

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

那我们一般什么情况下使用该函数呢?

errno的介绍

首先要知道当库函数调用失败的时候,会将错误码记录到errno这个变量中,errno是记录最后一次错误码的。代码是一个int类型的值,在errno.h中定义,所以使用时需要包含errno.h头文件

errno是C语言规定的一个全局变量,用来存储错误码

然后先暂时了解一下文件操作函数,fopen是用来开辟文件信息区将文件信息放入信息区并返回该文件信息区的函数,然后我们就可以通过FILE*类型的指针接收这个fopen返回的文件信息区的地址。

所以当我们调用fopen函数失败时,此时打开文件失败的错误信息的错误码会记录在errno变量中,我们就可以通过这个strerror将这个错误码对应的错误信息打印出来:

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

perror库函数的介绍:

还有一个库函数叫perror,和上面的strerror一样,也是打印错误信息的,唯一不同的区别就是返回类型。

perror和strerror的区别:

  • strerror接收到错误码是找到对应的错误信息的地址并返回,我们想打印就打印,不想打印就暂时存起来。
  • perror做的就有点绝,它不返回错误信息的起始地址,而是接收到错误码后直接在函数内部打印错误信息。
  • 注:perror不需要穿参,是可以直接获取errno的错误码打印出错误信息。

这就是strerror和perror的区别。

到这里,本篇C语言的字符函数和字符串函数也就结束了,如果有什么问题就请在评论区留言,下一篇C语言笔记再见

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

MyBatis一、MyBatis简介

MyBatis一、MyBatis简介 MyBatis 简介MyBatis 定义MyBatis 历史MyBatis 特性1. 灵活性和易用性2. 性能优化3. 易于集成4. 支持多种数据库5. 插件机制6. 其他特性 MyBatis 下载和其他持久化层技术对比 MyBatis 简介 MyBatis 定义 MyBatis 是一个优秀的持久层框架&#xff0c;它…

240602-通过命令行实现HuggingFace文件上传

A. 登录显示 A.1 MacOS A.2 Windows B. 操作步骤 B.1 操作细节 要通过命令行将文件上传到 Hugging Face&#xff0c;可以使用 huggingface-cli 工具。以下是详细步骤&#xff1a; 安装 huggingface_hub 包&#xff1a; 首先&#xff0c;确保已经安装了 huggingface_hub 包。可…

MySQL—函数—数值函数(基础)

一、引言 首先了解一下常见的数值函数哪些&#xff1f;并且直到它们的作用&#xff0c;并且演示这些函数的使用。 二、数值函数 常见的数值函数如下&#xff1a; 注意&#xff1a; 1、ceil(x)、floor(x) &#xff1a;向上、向下取整。 2、mod(x,y)&#xff1a;模运算&#x…

Wpf 使用 Prism 开发MyToDo应用程序

MyToDo 是使用 WPF &#xff0c;并且塔配Prism 框架进行开发的项目。项目中进行了前后端分离设计&#xff0c;客户端所有的数据均通过API接口获取。适合新手入门学习WPF以及Prism 框架使用。 首页统计以及点击导航到相关模块功能待办事项增删改查功能备忘录增删改查功能登录注册…

跨模型知识融合:大语言模型的知识融合

大语言模型&#xff08;LLMs&#xff09;在多个领域的应用日益广泛&#xff0c;但确保它们的行为与人类价值观和意图一致却充满挑战。传统对齐方法&#xff0c;例如基于人类反馈的强化学习&#xff08;RLHF&#xff09;&#xff0c;虽取得一定进展&#xff0c;仍面临诸多难题&a…

7-18 对象关系映射(orm_name)---PTA实验C++

一、题目描述 一开始看到对象关系映射&#xff0c;其实我是拒绝的。这三个词凑一块&#xff0c;能是给C初学者的题吗&#xff1f; 再仔细读需求&#xff0c;才发现在课设项目已经用过这功能。Object Relational Mapping&#xff08;ORM&#xff09;就是面向对象&#xff08;O…

大降分!重邮计算机专硕复试线大降50分!重庆邮电计算机考研考情分析!

重庆邮电大学&#xff08;Chongqing University of Posts and Telecommunications&#xff09;简称重邮&#xff0c;坐落于中国重庆市主城区南山风景区内&#xff0c;是中华人民共和国工业和信息化部与重庆市人民政府共建的教学研究型大学&#xff0c;入选国家“中西部高校基础…

【30天精通Prometheus:一站式监控实战指南】第13天:graphite_exporter从入门到实战:安装、配置详解与生产环境搭建指南,超详细

亲爱的读者们&#x1f44b;   欢迎加入【30天精通Prometheus】专栏&#xff01;&#x1f4da; 在这里&#xff0c;我们将探索Prometheus的强大功能&#xff0c;并将其应用于实际监控中。这个专栏都将为你提供宝贵的实战经验。&#x1f680;   Prometheus是云原生和DevOps的…

企业im即时通讯WorkPlus私有化部署适配国产信创环境

在信息化时代&#xff0c;高效的沟通和协作对于企业的运营至关重要。企业IM即时通讯平台提供了一种便捷、实时的沟通工具&#xff0c;旨在改善企业的内部和外部沟通效率。然而&#xff0c;随着企业对数据安全性和隐私保护的要求不断提高&#xff0c;许多企业开始选择私有化部署…

【Qt知识】disconnect

在Qt框架中&#xff0c;disconnect函数用于断开信号与槽之间的连接。当不再需要某个信号触发特定槽函数时&#xff0c;或者为了防止内存泄漏和重复执行问题&#xff0c;你可以使用disconnect来取消这种关联。disconnect函数的基本用法可以根据不同的需求采用多种形式&#xff0…

【ORB_SLAM系列3】—— 如何在Ubuntu18.04中使用自己的单目摄像头运行ORB_SLAM3(亲测有效,踩坑记录)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、ORB_SLAM3源码编译二、ORB_SLAM3实时单目相机测试1. 查看摄像头的话题2. 运行测试 三. 运行测试可能的报错1. 报错一(1) 问题描述(2) 原因分析(3) 解决 2. …

Windows下如何把Oracle从C盘整体迁移到D盘?

&#xff08;一&#xff09;写这篇文章的起因 这篇文章适合刚接触的技术小白follow操作&#xff0c;整理文章不易&#xff0c;大家多多点赞转发 起因是昨天有会员在群里发问&#xff0c;客户要把Oracle整个目录从C盘挪到D盘怎么弄 客户那边的人把Oracle整个程序数据文件都安装…

使用 Kali Linux 实现 Smurf 攻击

一、介绍 Smurf攻击是一种分布式拒绝服务&#xff08;DDoS&#xff09;攻击&#xff0c;利用IP协议中的ICMP&#xff08;Internet Control Message Protocol&#xff09;请求和网络的广播特性&#xff0c;使目标系统被大量ICMP回复包淹没&#xff0c;从而导致系统无法正常提供…

ZDH-数据管理模块

目录 主题 项目源码 预览地址 安装包下载地址 数据管理服务 数据资源管理 数据资源权限 数据资源血缘 总结 感谢支持 主题 本篇文章主要介绍ZDH-数据管理服务及应用场景 项目源码 zdh_web: GitHub - zhaoyachao/zdh_web: 大数据采集,抽取平台 预览地址 后台管理…

【C++】类和对象——构造和析构函数

目录 前言类的六个默认构造函数构造函数1.构造函数的概念2.构造函数的特性 初始化列表1.构造函数整体赋值2.初始化列表 析构函数1.析构函数的概念2.析构函数的特性 前言 类和对象相关博客&#xff1a;【C】类和对象   我们前面一个内容已经讲了关于类好对象的初步的一些知识&…

绿联 安装MariaDB数据库用于Seatable服务

绿联 安装MariaDB数据库用于Seatable服务 1、镜像 mariadb:latest 2、安装 2.1、基础设置 重启策略&#xff1a;容器退出时总是重启容器。 2.2、网络 网络选择桥接(bridge)。 2.3、存储空间 装载路径/var/lib/mysql不可变更。 2.4、端口设置 容器端口3306&#xff0c;本…

7. MySQL 视图、索引

文章目录 【 1. 视图 View 】1.1 视图原理1.2 创建视图 CREATE VIEW1.2.1 创建基于单表的视图1.2.2 创建基于多表的视图 1.3 查看视图1.3.1 查看视图的内容1.3.2 查看视图的详细信息 1.4 修改视图 ALTER VIEW1.4.1 修改视图内容1.4.2 修改视图名称 1.5 删除视图 DORP VIEW 【 2…

Ansys Mechanical|组装 External Mechanical Model

Assembling Finite Element Models 上文中介绍了如何导入外部模型并将其组合到单个模型中的示例。 如果要将外部模型与Workbench环境中已有的一个或多个模型组合在一起&#xff0c;该如何操作&#xff1f;本文将介绍这个工作流程。 Ansys Mechanical支持Mechanical Model和Ex…

移动系统编程-安装和运行Ionic应用程序 (Installation and Running Ionic Apps)

安装 (Installation) 假设您已经安装了Node.js和Angular&#xff0c;您可以使用以下命令安装Ionic&#xff1a; npm install -g ionic/cli您也可以不使用CLI安装Ionic&#xff0c;但如果您使用的是最新版本的Cordova&#xff0c;这样做可能会导致版本不匹配&#xff0c;不推荐…