【C语言(十)】

字符函数和字符串函数

一、字符分类函数

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

这些函数的使用方法非常类似,我们就讲解⼀个函数的事情,其他的非常类似: 

int islower ( int c );  

islower 是能够判断参数部分的 c 是否是小写字母的。 通过返回值来说明是否是小写字母,如果是小写字母就返回非0的整数,如果不是小写字母,则返回0。

练习:

写⼀个代码,将字符串中的小写字母转大写,其他字符不变。

#include <stdio.h>
#include <ctype.h>
int main ()
{int i = 0;char str[] = "Test String.\n";char c;while (str[i]){c = str[i];if (islower(c)) c -= 32;putchar(c);i++;}return 0;
}
//把一个字符串转换成全小写
int main()
{char arr[] = "I Am A Student";int i = 0;while (arr[i]){if (isupper(arr[i])){//arr[i] = arr[i] + 32;arr[i] = tolower(arr[i]);}putchar(arr[i]);i++;}return 0;
}

二、字符转换函数 

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

上面的代码,我们将小写转大写,是-32完成的效果,有了转换函数,就可以直接使用 tolower 函数。

#include <stdio.h>
#include <ctype.h>
int main ()
{int i = 0;char str[] = "Test String.\n";char c;while (str[i]){c = str[i];if (islower(c)) c = toupper(c);putchar(c);i++;}return 0;
}

三、strlen的使用和模拟实现

字符串以 '\0' 作为结束标志,strlen函数返回的是在字符串中 '\0' 前⾯出现的字符个数(不包
'\0' )。
参数指向的字符串必须要以 '\0' 结束。
注意函数的返回值为size_t,是无符号的( 易错 )
strlen的使用需要包含头文件
学会strlen函数的模拟实现
int main()
{//3 - 6 = -3 --> 无符号整型 - 无符号整型 = 无符号整型,故-3被当成无符号整型,所以它是一个非常大的正数if (strlen("abc") - strlen("abcdef") > 0){printf("大于\n");}else{printf("小于等于\n");}return 0;
}

改良: 

int main()
{if ((int)strlen("abc") - (int)strlen("abcdef") > 0){printf("大于\n");}else{printf("小于等于\n");}return 0;
}

从这个代码中其实可以学到很多知识:

①unsigned int类型比int类型容量大,因为对于32/64位机器最前面一位对于int类型而言均是符号位,而对于unsigned int类型最高位不是符号位是可以存数据的。

②,当unsigned int类型和int类型运算时,如果没有指出转化类型,它会进行隐式转换为unsigned int 类型。同时当两个类型运算要进行隐式转换时一般会转换成容量较大的那个。

③printf打印的是%d,是整型,所以unsigned int 会转换成int类型,故a+b的值打印出就是3+-6=-3

④对于if ()逻辑语句中的a+b中的int类型便会转换为unsigned int 类型,-6对应的int类型原码为1000 0000 0000 0000 0000 0000 0000 0110,反码是1111 1111 1111 1111 1111 1111 1111 1001,补码为1111 1111 1111 1111 1111 1111 1111 1010,故补码的这串数会直接放进unsigned int 中,对应数为4294967290,再加上3为4294967293>0,所以输出为大于。

strlen的模拟实现: 

size_t my_strlen1(const char* str)
{size_t count = 0;assert(str != NULL);while (*str){count++;str++;}return count;
}size_t my_strlen2(const char* str)
{assert(str);char* start = str;while (*str){str++;}return str - start;
}
//写一个函数,不能创建临时变量,求字符串长度 - 递归
//my_strlen("abcdef")
//1+my_strlen("bcdef")
//1+1+my_strlen("cdef")
//1+1+1+my_strlen("def")
//1+1+1+1+my_strlen("ef")
//1+1+1+1+1+my_strlen("f")
//1+1+1+1+1+1+my_strlen("")
//1+1+1+1+1+1+0
size_t my_strlen(const char* str)
{if (*str == '\0')return 0;elsereturn 1 + my_strlen(str + 1);
}int main()
{char arr[] = "abcdef";size_t len = my_strlen(arr);printf("%zd\n", len);return 0;
}

补充: 

NULL --->  本质也是0,一般用于指针的初始化

\0 ---> \ddd 形式的转义字符,本质也是0,一般字符串的末尾会有\0是字符串的结束标志

0 ---> 数字0

null(NUL) ---> \0

'0' ---> 字符0,本质是 48

四、strcpy的使用和模拟实现 

Copies the C string pointed by source into the array pointed by destination, including the
terminating null character (and stopping at that point).
源字符串必须以 '\0' 结束。
会将源字符串中的 '\0' 拷贝到目标空间。
⽬标空间必须足够大,以确保能存放源字符串。
⽬标空间必须可修改。
学会模拟实现。

strcpy的使用:  

int main()
{//char arr1[5] = { 0 };//目标空间必须足够大//char arr2[] = "hello world";//strcpy(arr1, arr2);//printf("%s\n", arr1);char* p = "abcdefghilmkqwer";//常量字符串,不能修改char arr2[] = "hello world";strcpy(p, arr2);//目标空间必须是可修改的printf("%s\n", p);return 0;
}

 strcpy的模拟实现:

//模拟实现strcpy函数
char* my_strcpy(char* dest, const char* src)
{char* ret = dest;/*assert(dest != NULL);assert(src != NULL);*//*assert(dest);assert(src);*/assert(dest && src);//while (*src)//{//	//*dest = *src;//	//dest++;//	//src++;//	*dest++ = *src++;//}//*dest = *src;while (*dest++ = *src++);return ret;
}int main()
{char arr1[20] = { 0 };char arr2[] = "abcdef";printf("%s\n", my_strcpy(arr1, arr2));return 0;
}

五、strcat的使用和模拟实现 

Appends a copy of the source string to the destination string. The terminating null character in destination is overwritten by the first character of source, and a null-character is included
at the end of the new string formed by the concatenation of both in destination.
源字符串必须以 '\0' 结束。
⽬标字符串中也得有 \0 ,否则没办法知道追加从哪⾥开始。
⽬标空间必须有足够的⼤,能容纳下源字符串的内容。
⽬标空间必须可修改。
字符串不能自己给自己追加,会产生覆盖。
char* my_strcat(char* dest, const char* src)
{char* ret = dest;assert(dest && src);//1.找到目标空间的\0while (*dest){dest++;}//2.拷贝while (*dest++ = *src++);return ret;
}int main()
{char arr1[20] = "hello ";char arr2[] = "world";my_strcat(arr1, arr2);printf("%s\n", arr1);return 0;
}

 六、strcmp的使用和模拟实现

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.
标准规定:
第⼀个字符串大于第⼆个字符串,则返回大于0的数字
第⼀个字符串等于第⼆个字符串,则返回0
第⼀个字符串小于第⼆个字符串,则返回小于0的数字
那么如何判断两个字符串?比较两个字符串中对应位置上字符ASCII码值的大小。

strcmp函数的模拟实现: 

int my_strcmp (const char * str1, const char * str2)
{int ret = 0 ;assert(src != NULL);assert(dest != NULL);while(*str1 == *str2){if(*str1 == '\0')return 0;str1++;str2++;}return *str1-*str2;
}

补充:

%zd 用来格式化 ssize_t 类型(有符号整数类型)或 size_t 类型(无符号整数类型)的值。这个占位符是用于确保正确的格式化,并目可以在有符号和无符号整数之间正确切换。

当你使用 %zd 时,它可以用于 ssize_t 和 size_t 类型,因此适用于带符号和无符号整数。

那么%d和%zd的区别到底在哪里呢?

区别在于 %d 适用于有符号整数,而 %zd 适用于既有符号整数又有无符号整数,可以避免数据类型不匹配的问题。如果你要输出size_t类型的值,最好使用 %zd 以确保正确的格式化。

简而言之,如果你要输出无符号整数类型(如 size_t),请使用 %zd 以避免潜在的问题。如果你要输出有符号整数,那么可以使用%d。

那么我们继续来讨论%zu的作用:

%zu是用来格式化size_t类型的整数值的标准占位符。 (这并不难理解,因此我们简略介绍)

总结:

%d是仅用来打印有符号整型

%zd则既能打印无符号整型又能打印有符号整型

%zu是size_t的标准占位符

七、strncpy函数的使用 

Copies the first num characters of source to destination. If the end of the source C string
(which is signaled by a null-character) is found before num characters have been copied,
destination is padded with zeros until a total of num characters have been written to it.
拷贝num个字符从源字符串到目标空间。
如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个。

八、strncat函数的使用

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

Appends the first num characters of source to destination, plus a terminating null-character. (将source指向字符串的前num个字符追加到destination指向的字符串末尾,再追加⼀个 \0 字符)。
If the length of the C string in source is less than num, only the content up to the terminating null-character is copied.(如果source 指向的字符串的长度小于num的时候,只会将字符串中到 \0 的内容追加到destination指向的字符串末尾)。
/* strncat example */
#include <stdio.h>
#include <string.h>
int main ()
{char str1[20];char str2[20];strcpy (str1,"To be ");strcpy (str2,"or not to be");strncat (str1, str2, 6);printf("%s\n", str1);return 0;
}

我们可以测试看一看strncpy时会不会把\0也复制过来,经过下面的测试我们发现确实如我们所想,\0也会跟着被复制过来。并且如果我们选择复制的源头的复制个数超过本来的字符串长度,那么将会把多出来的用\0来填补。

我们再来刨析一下strncat的不同使用情况:

strncat在插入字符串之后,如若源字符串长度比要插入的长度大,则会在插入后多补一个\0,若源字符串长度比要插入的长度小,则只会在插入后补充一个\0,多余的长度则不做计较。

九、strncmp函数的使用 

较str1和str2的前num个字符,如果相等就继续往后较,最多较num个字母,如果提前发现不⼀样,就提前结束,大的字符所在的字符串⼤于另外⼀个。如果num个字符都相等,就是相等返回0。

十、strstr的使用和模拟实现 

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

Returns a pointer to the first occurrence of str2 in str1, or a null pointer if str2 is not part of str1.
(函数返回字符串str2在字符串str1中第⼀次出现的位置)。
The matching process does not include the terminating null-characters, but it stops there.(字符
串的比较匹配不包含 \0 字符,以 \0 作为结束标志)。
/* strstr example */
#include <stdio.h>
#include <string.h>
int main ()
{char str[] ="This is a simple string";char * pch;pch = strstr (str,"simple");strncpy (pch,"sample",6);printf("%s\n", str);return 0;
}

 strstr的模拟实现:

//暴力算法
char* my_strstr(const char* str1, const char* str2)
{const char* cur = str1;const char* s1 = NULL;const char* s2 = NULL;assert(str1 && str2);if (*str2 == '\0')return (char*)str1;while (*cur){s1 = cur;s2 = str2;while (*s1 && *s2 && *s1 == *s2)//1.s1找到了\0,*s2 == '\0' -> 找到了//2.s2找到了\0 -> 找到了//3.*s1 != *s2{s1++;s2++;}if (*s2 == '\0')return (char*)cur;cur++;}return NULL;
}int main()
{char arr1[] = "abbbcdef";char arr2[] = "bbc";char* ret = my_strstr(arr1, arr2);if (ret != NULL)printf("%s\n", ret);elseprintf("找不到!\n");return 0;
}

十一、strtok函数的使用 

sep参数指向⼀个字符串,定义了用作分隔符的字符集合
第⼀个参数指定⼀个字符串,它包含了0个或者多个由sep字符串中⼀个或者多个分隔符分割的标记。
strtok函数找到str中的下⼀个标记,并将其用 \0 结尾,返回⼀个指向这个标记的指针。(注:strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串⼀般都是临时拷贝的内容并且可修改。)
strtok函数的第⼀个参数不为NULL ,函数将找到str中第⼀个标记,strtok函数将保存它在字符串中的位置。
strtok函数的第⼀个参数为 NULL ,函数将在同⼀个字符串中被保存的位置开始,查找下⼀个标记。
如果字符串中不存在更多的标记,则返回 NULL 指针。
int main()
{char arr[] = "zhangsan@163.com";char arr2[20] = { 0 };//zhangsan@163.com -> zhangsan\0163\0comstrcpy(arr2, arr);const char* p = "@.";char* s = NULL;//char* s = strtok(arr2, p);//zhangsan\0//printf("%s\n", s);//s = strtok(NULL, p);//163\0//printf("%s\n", s);//s = strtok(NULL, p);//com\0//printf("%s\n", s);//初始化部分只执行一次for (s = strtok(arr2, p); s != NULL; s = strtok(NULL, p))printf("%s\n", s);return 0;
}

strtok0)用来将字符串分割成一个个片段。参数str指向欲分割的字符串,参数sep则为分割字符串中包含的所有字符。当strtok()在参数s的字符串中发现参数sep中包含的分割字符时,则会将该字符改为\0 字符。在第一次调用时,strtok()必需给予参数str字符串,往后的调用则将参数str设置成NULL。每次调用成功则返回指向被分割出片段的指针。

从str开头开始的一个个被分割的串。当str中的字符查找到未尾时,返回NULL。

如果查找不到sep中的字符时,返回当前strtok的字符串的指针。

所有sep包含的字符都会被滤掉,并将被滤掉的地方设为一处分割的节点。

十二、strerror函数的使用 

char * strerror ( int errnum );

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

在不同的系统和C语言标准库的实现中都规定了⼀些错误码,⼀般是放在 errno.h 这个头文件中说明的,C语⾔程序启动的时候就会使用⼀个全局的变量errno来记录程序的当前错误码,只不过程序启动的时候errno是0,表示没有错误,当我们在使用标准库中的函数的时候发生了某种错误,就会讲对应的错误码,存放在errno中,而⼀个错误码的数字是整数很难理解是什么意思,所以每⼀个错误码都是有对应的错误信息的。strerror函数就可以将错误对应的错误信息字符串的地址返回。
#include <errno.h>
#include <string.h>
#include <stdio.h>
//我们打印⼀下0~10这些错误码对应的信息
int main()
{int i = 0;for (i = 0; i <= 10; i++) {printf("%s\n", strerror(i));}return 0;
}

 在Windows11+VS2022环境下输出的结果如下:

No error
Operation not permitted
No such file or directory
No such process
Interrupted function call
Input/output error
No such device or address
Arg list too long
Exec format error
Bad file descriptor
No child processes

举例: 

#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;
}

 输出:

也可以了解⼀下perror函数,perror函数相当于⼀次将上述代码中的第9行完成了,直接将错误信息打印出来。perror函数打印完参数部分的字符串后,再打印⼀个冒号和⼀个空格,再打印错误信息。
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main ()
{FILE * pFile;pFile = fopen ("unexist.ent","r");if (pFile == NULL)perror("Error opening file unexist.ent");return 0;
}

输出:Error opening file unexist.ent: No such file or directory.

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

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

相关文章

华为OD机试真题-多段线数据压缩-2023年OD统一考试(C卷)

题目描述: 下图中,每个方块代表一个像素,每个像素用其行号和列号表示。 为简化处理,多段线的走向只能是水平、竖直、斜向45度。 上图中的多段线可以用下面的坐标串表示:(2, 8), (3, 7), (3, 6), (3, 5), (4, 4), (5, 3), (6, 2), (7, 3), (8, 4), (7, 5)。 但可以发现,这…

springboot整合webflux访问openai接口报错

报错信息: org.springframework.web.reactive.function.client.WebClientRequestException: 远程主机强迫关闭了一个现有的连接。; nested exception is java.io.IOException: 远程主机强迫关闭了一个现有的连接。at org.springframework.web.reactive.function.client.Excha…

栈和队列的实现(Java篇)

文章目录 一、栈的概念二、栈的实现2.1压栈(push)2.2出栈(pop)2.3获取栈顶元素(peek)2.4判断栈是否为空(isEmpty)栈的实现测试 三、队列的概念四、队列的实现4.1入队(offer)4.2出队(poll)4.3判断队列是否为空4.4获取对头元素队列的实现测试 五、循环队列5.1入队5.2出队5.3获取队…

MAMOS蓝图: 打造自己的质量工程

针对团队中存在的问题&#xff0c;构造MAMOS蓝图&#xff0c;从而以系统化的方式识别并解决问题。本文将针对减少等待时间这一问题举例说明MAMOS蓝图的组成和使用方式。原文: MAMOS Blueprint: Build your own for Quality at Speed 很难完全摆脱等待时间。 我认为没有必要争论…

vue脚手架安装及使用

准备工作 安装node安装cnpm cnpm是npm的“廉价平替” 提高安装速度 npm install -g cnpm --registryhttps://registry.npm.taobao.org 安装脚手架 安装Vue脚手架 cnpm install -g vue/cli 用vue脚手架创建vue项目 找好创建项目的位置 创建项目 vue create test (test为项…

用代码写uml并在线生成uml图

可以用PlantUml写uml,并在线生成uml图。 startuml start:登录系统; if (用户名和密码正确?) then (yes):进入系统首页;:展示主菜单; else (no):显示登录错误;stop endif:选择模块; partition "课程信息" {:查看课程列表;:查看课程详情; } partition "课程签到…

【计算机视觉】SIFT

在边缘提取的时候&#xff0c;用高斯一阶导对信号进行卷积&#xff0c;响应值最大的就是边界如果用高斯二阶导对信号进行卷积&#xff0c;0点就是边界点&#xff08;二阶导等于0的点&#xff0c;对应一阶导的极值点&#xff09; 如果用高斯二阶导在不同的信号上进行卷积&#x…

python requests.Session()的使用

cookies and jar 你可以把Session理解成为它内部提供了一系列方法&#xff0c;用来存储状态。这里所说的状态&#xff0c;其实大部分情况下指的就是cookies。 比如使用浏览器时候&#xff0c;你在一个标签页内登陆了&#xff0c;另一个标签页其实就可以读取cookies了&#xf…

口袋参谋:新品上架,如何获取更多免费流量?

​新品上架 如何获得更多的免费流量&#xff1f; 我相信 这是99.999%的商家&#xff0c;都关心的问题&#xff01; 今天我就来和大家好好说道说道。 01 流量的组成 新品本身是没有权重的&#xff0c;买家搜不到我们。 如果想要获得更多的免费流量&#xff0c;我们就要知道…

基础算法(1):排序(1):选择排序

今天对算法产生了兴趣&#xff0c;开始学习基础算法&#xff0c;比如排序&#xff0c;模拟&#xff0c;贪心&#xff0c;递推等内容&#xff0c;算法是很重要的&#xff0c;它是解决某个问题的特定方法&#xff0c;程序数据结构算法&#xff0c;所以对算法的学习是至关重要的&a…

出现 Error:Unable to access jarfile xxxx\target\nacos-server.jar 解决方法

目录 1. 问题所示2. 原理分析3. 解决方法1. 问题所示 执行Nacos中的startup.cmd的时候出现闪退,于是在该脚本的最后一行添加pause,查看因为什么原因闪退 出现的bug如下所示:Error:Unable to access jarfile xxxx\target\nacos-server.jar 截图如下所示: 查看内部文件夹,…

塑料检查井配套开发了注塑成型的井盖、井筒、井座

塑料检查井配套开发的注塑成型井盖、井筒、井座——城市基础设施的新选择 随着城市化进程的加快&#xff0c;城市基础设施建设的品质与效率日益受到重视。在这个背景下&#xff0c;塑料检查井及其配套开发的注塑成型井盖、井筒、井座以其独特的优势&#xff0c;正在逐渐取代传…

k8s debug 浅谈

一 k8s debug 浅谈 说明&#xff1a; 本文只是基于对kubectl debug浅显认识总结的知识点,后续实际使用再补充案例 Kubernetes 官方出品调试工具上手指南(无需安装&#xff0c;开箱即用) debug-application 简化 Pod 故障诊断: kubectl-debug 介绍 1.18 版本之前需要自己…

DevEco Studio自定义代码颜色

这里以ArkTS代码颜色举例 进入设置&#xff08;快捷键CtrlAltS&#xff09; 选择Editor > Color Scheme > JavaScript 由于之前用习惯VsCode了&#xff0c;这里以注释颜色举例&#xff0c;变为绿色。 上面说的不是以ArkTS代码颜色举例吗&#xff1f;为什么选择JavaScr…

054:vue工具 --- BASE64加密解密互相转换

第054个 查看专栏目录: VUE ------ element UI 专栏目标 在vue和element UI联合技术栈的操控下&#xff0c;本专栏提供行之有效的源代码示例和信息点介绍&#xff0c;做到灵活运用。 &#xff08;1&#xff09;提供vue2的一些基本操作&#xff1a;安装、引用&#xff0c;模板使…

探索泰勒级数在机器学习中的作用:从函数逼近到模型优化

一、介绍 泰勒级数是数学中的一个基本概念&#xff0c;在机器学习领域有着重要的应用。本文将探讨泰勒级数的基础知识、它在机器学习中的相关性以及一些具体应用。 揭开复杂性&#xff1a;利用泰勒级数增强机器学习应用的理解和效率。 二、理解泰勒级数 在数学中&#xff0c;泰…

物联网AI 物联网平台学习之概述

学物联网&#xff0c;来万物简单IoT物联网&#xff01;&#xff01; 万物简单IOT是一个集物联网教育、企业SaaS私有化部署的物联网服务平台&#xff0c;它集成了设备管理、数据安全通信、消息订阅、规则引擎等一系列物联网核心能力&#xff0c;支持设备数据上云以及海量设备数…

大 O 表示法在机器学习中的重要性

一、介绍 在不断发展的机器学习领域&#xff0c;算法的效率至关重要。大 O 表示法成为这方面的一个关键工具&#xff0c;它提供了一种描述算法性能或复杂性的语言&#xff0c;特别是在时间和空间方面。本文探讨了 Big O 表示法在机器学习中的重要性&#xff0c;阐明了它在算法选…

Linux开发工具--vim

Linux开发工具--vim 一、vim的基本概念二、常见命令三、简单配置vim配置文件的位置常用配置选项&#xff0c;用来测试使用插件 一、vim的基本概念 vim编辑器&#xff0c;只负责写代码&#xff0c;vim是一款多模式的编辑器 vim的三种模式(其实有好多模式&#xff0c;目前掌握这…

服务器数据恢复—raid5热备盘未激活崩溃导致上层oracle数据丢失的数据恢复案例

服务器数据恢复环境&#xff1a; 某品牌X系列服务器&#xff0c;4块SAS硬盘组建了一组RAID5阵列&#xff0c;还有1块磁盘作为热备盘使用。服务器上层安装的linux操作系统&#xff0c;操作系统上部署了一个基于oracle数据库的OA&#xff08;oracle已经不再为该OA系统提供后续服务…