在21世纪的我用C语言探寻世界本质——字符函数和字符串函数(2)

news/2025/3/6 13:00:32/

在这里插入图片描述

人无完人,持之以恒,方能见真我!!!
共同进步!!

文章目录
  • 一、strncpy函数的使用
  • 二、strncat函数的使用
  • 三、strncmp函数的使用
  • 四、strstr的使用和模拟实现
  • 五、strtok函数的使用
  • 六、strerror和perror函数的使用
    • 1.strerror函数
    • 2.perror函数

一、strncpy函数的使用

我们之前学习的strcpy的作用是把源字符串拷贝到目标空间内,而且经过我们的模拟实现,我们也意识到它拷贝的时候会把目标空间的内容给替换了,我们可以来测试一下:

在这里插入图片描述

可以看到,将arr2的内容拷贝到arr1中时,把arr1原本的内容替换了,那假设我们不想让它全部拷贝过来,只是拷取一部分OK不OK

这个时候就要引入我们带n的字符串函数,那个多出来的n就代表数量,是我们想拷贝字符串时,要拷贝的字符的个数
而不带n的字符串函数和带n的字符串函数的根本区别是带n的字符串函数更灵活,可以指定拷贝的字符的个数,所以也叫带n的字符串函数为受限制的字符串函数,不带n的为不受限制的字符串函数

接下来我们来看看本次讲到的strncpy函数的原型:

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

它的参数与strcpy的区别就是多了最后一个参数,它的作用就是指定我们要拷贝的字符的个数
它的特点如下:

  • 拷num个字符从源字符串到标空间
  • 如果源字符串的度于num,则拷完源字符串之后,在标的后边追加,直到num个
#include <stdio.h>
#include <string.h>int main()
{char arr1[20] = "abcdef";char arr2[20] = "ghijklmn";strncpy(arr1, arr2, 3);printf("%s
", arr1);return 0;
}

在这里插入图片描述

可以看到只拷贝了arr2中的前三个字符进arr1,并且覆盖了arr1的前三个字符,这就是strncpy的作用
至于strncpy的模拟实现,与strcpy的模拟实现相似,这里就不再过多讲解

二、strncat函数的使用

strncat也是带n家族的一员,多n的原因和strncpy差不多,就是用来指定要追加到目标空间的字符的个数,它的原型如下:

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

它的特点是:

  • 将source指向字符串的前num个字符追加到destination指向的字符串末尾,再追加个 字符
  • 如果source指向的字符串的字符个数于num的时候,只会将字符串中到 的内容追加到destination指向的字符串末尾

了解到这里我们来尝试使用一下strncat,把arr2的前3个字符追加到arr1里,如下:

#include <stdio.h>
#include <string.h>int main()
{char arr1[20] = "abcdef";char arr2[20] = "ghijklmn";strncat(arr1, arr2, 3);printf("%s
", arr1);return 0;
}

运行结果:

在这里插入图片描述

如果后面的第三个参数num大于arr2的大小会发生什么呢?
运行结果:

在这里插入图片描述

调试结果:

在这里插入图片描述

三、strncmp函数的使用

strncmp的作用就是比较str1和str2的前num个字符,如果相等就继续往后较,最多较num个字

如果在这num个字符中发现不样,就看此时哪个字符串更大,前者大就返回大于0的数,如果后者大,就返回小于0的数如果num个字符都相等,就是相等返回0

我们来看看它的原型:

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

我们来使用它比较arr1和arr2前三个字符的大小:

#include <stdio.h>
#include <string.h>int main()
{char arr1[20] = "abcdef";char arr2[20] = "ghijklmn";int ret = strncmp(arr1, arr2, 3);printf("在前三个字符的比较中,");if (ret > 0)printf("arr1 > arr2
");else if (ret < 0)printf("arr1 < arr2
");elseprintf("arr1 = arr2
");return 0;
}

运行结果:

在这里插入图片描述

四、strstr的使用和模拟实现

这是一个对我们来说比较新的函数,我们来仔细介绍一下使用方法以及它的模拟实现,它的作用就是返回后一个字符串在前一个字符串中第次出现的位置

并且字符串的较匹配不包含 字符,以 作为结束标志

接下来我们来看看strstr的原型:

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

它的原型中参数看起来很简单,就是两个字符串,那它的返回值是啥捏?
有两种情况:

  • 如果在字符串str1中找到了字符串str2,那么就返回str1中找到str2的起始位置,
  • 如果没有在字符串str1中找到字符串str2,那么就返回一个NULL

所以在使用的时候,我们就可以通过它的返回值来确认是否找到对应的字符串,如果返回非空指针,那么就是找到了,返回空指针就是没有找到,接下来我们用实例来加深理解:

#include <stdio.h>
#include <string.h>void isfind(const char* x)
{if (x)printf("找到了
");elseprintf("没找到
");
}int main()
{char arr1[20] = "abcdef";char arr2[20] = "ghijklmn";char arr3[20] = "cde";isfind(strstr(arr1, arr2));isfind(strstr(arr1, arr3));return 0;
}

运行结果:

在这里插入图片描述

我们来模拟实现一下 strstr

  1. 函数命名:my_strstr
  2. 函数参数:可以照抄strstr函数的原型,如下:
const char* my_strstr ( const char* str1, const char* str2);
  1. 函数实现:
    (1)老样子,assert进行断言
    (2)然后我们来分析这个函数可能出现的情况,按照我们正常去想,就会想到首先就去看str1和str2指向的字符相不相等,不相等就两个指针都++,往后找,直到它们指向的字符相等
    (3)随后往后开始找,但是会出现一个问题,万一这一次匹配不上,只有前几个字符相等,后面并不相等,那我们怎么找到之前开始匹配的位置呢?如图:

在这里插入图片描述

(4)所以我们让str1和str2直接往后走是不妥的,这样一旦匹配失败,我们就记不住当时开始匹配的位置,也就不能进行下一次匹配,所以我们可以重新创建三个指针变量
(5)有两个用来装下str1,一个用来代替str1往后走,一个用来记住当前第一次匹配的位置,至于str1就老老实实待在原地,不要动
(6)还有一个用来装str2,这样如果匹配失败,就把str2重新赋值给这个变量,让它重新指向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* cp = str1;//记录开始匹配字符的位置
}

(7)有了以上的经验,我们为了避免犯错,可以使用思路比较简单的暴力匹配,就是创建一个循环,我们对cur进行解引用,只要里面不是结束标志,我们就进入循环,然后看这个位置的字符是否和str2的第一个字符相等

(8)如果不相等,我们就让cp++,让我们的当前位置往前挪动一下,如果相等,就进行完整的匹配,这里还是有两种情况,一是如果完全匹配那么就返回cp,就是我们开始匹配时的位置,二是如果不能完全匹配那么就还是让cp++,并且将cp重新赋值给s1,将str2重新赋值给s2

(9)现在我们就来看进行一次匹配的过程,也很简单,我们还是创建一个while循环,如果s1和s2解引用不是 ,并且解引用后相等,那我们就让s1和s2加加,一直往后走,当循环结束时,我们就判断s2解引用后是否是 ,如果是说明完全匹配上了,就返回cp,不是那么说明没有完全匹配,就让cp++,开始下一次循环查找

(10)最后一步就是,如果我们遍历了s1中的所有字符,还是发现两个字符串没有完全匹配的地方,就直接返回空指针NULL

const char* my_strstr(const char* str1, const char* str2)
{assert(str1 && str2);if(*str2 == ''){return (char*)str1;}const char* s1 = str1;const char* s2 = str2;const char* cp = str1;while (*cp){s1 = cp;//重新赋值s2 = str2;//重新赋值while (*s1 && *s2 && *s1 == *s2){s1++;s2++;}if (*s2 == '')return cp;cp++;}return NULL;
}

这是一般的情况:

在这里插入图片描述

我们换一种更复杂的情况测试:

在这里插入图片描述

可以看到我们实现的函数还是成功帮我们完成了任务,这里的strstr函数的实现就到此结束了,模拟实现这个函数要考虑多种情况,希望大家能够下来多敲敲,欢迎大家在评论区留言!

五、strtok函数的使用

这也是一个新函数,它的作用是什么呢?它可以按照我们给出的分隔符来分割我们的字符串,现在我们先来了解一下它的原型:

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

接下来我们来总结一下它的特点:

  • sep参数指向个字符串,定义了作分隔符的字符集合
  • 第个参数指定个字符串,它包含了0个或者多个由sep字符串中个或者多个分隔符分割的标记
  • strtok函数找到str中的下个标记,并将其 结尾,返回个指向这个标记的指针(注:strtok函数会改变被操作的字符串,所以被strtok函数切分的字符串般都是临时拷的内容并且可修改)
  • strtok函数的第个参数不为 NULL ,函数将找到str中第个标记,strtok函数将保存它在字符串
    中的位置
  • strtok函数的第个参数为 NULL ,函数将在同个字符串中被保存的位置开始,查找下个标记
  • 如果字符串中不存在更多的标记,则返回 NULL 指针

比如我们的邮箱由两个分隔符分隔开来,就是@符合和(.)点号,我们想把这个邮箱地址分隔开来可以这样操作:

#include <stdio.h>
#include <string.h>int main()
{char arr1[] = "BANZHANCHAXIANG@163.com";char arr2[] = "@.";char* ret1 = strtok(arr1, arr2);char* ret2 = strtok(NULL, arr2);char* ret3 = strtok(NULL, arr2);printf("%s
%s
%s
", ret1, ret2, ret3);return 0;
}

运行结果:
在这里插入图片描述
可以看到我们成功把这个邮箱分成了三个部分,但是有一个问题,这里有两个分隔符我们就写了三行代码,这样的代码看起来是不是很挫,而且如果有n个分隔符,就会变得很麻烦,这时候就可以用下面的方法

#include <stdio.h>
#include <string.h>int main()
{char arr1[] = "BANZHANCHAXIANG@163.com";char arr2[] = "@.";char* p = NULL;for (p = strtok(arr1, arr2); p != NULL; p = strtok(NULL, arr2)){printf("%s
", p);}return 0;
}

运行结果:

在这里插入图片描述

我们利用for循环初始化只进行一次的技巧来调用第一次的strtok,并且将它赋值给p指针,随后我们调用strtok的第一个参数就只需要传空指针,就可以写在循环的调整部分

而中间的循环结束条件为什么设置为不等于NULL呢?因为当我们的字符串分隔完毕,没有分隔符后,函数就会返回一个空指针,p也就等于空指针了,此时用来退出循环刚好

六、strerror和perror函数的使用

1.strerror函数

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

在不同的系统和C语标准库的实现中都规定了些错误码,般是放在errno.h 这个头件中说明的,C语程序启动的时候就会使个全局的变量errno来记录程序的当前错误码,只不过程序启动的时候errno是0,表没有错误

但是如果当我们在使标准库中的函数的时候发了某种错误,就会将对应的错误码,存放在errno中,而个错误码的数字是整数很难理解是什么意思,所以每个错误码都是有对应的错误信息的

strerror函数就可以将错误对应的错误信息字符串的地址返回,可以用%s的形式将错误信息打印出来

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

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

在这里插入图片描述

我们可以测试使用文件操作测试一下:

#include <stdio.h>
#include <string.h>
#include <errno.h>
int main ()
{FILE * pFile;pFile = fopen ("data.txt","r");if (pFile == NULL)printf ("fopen: %s
", strerror(errno));return 0;
}

在当前路径的代码目录上并没有data.txt这个文件,自然不能打开文件进行读操作,所以这个代码会在errno.h中产生一个错误码,这个时候我们就把这个错误码打印出来
在这里插入图片描述

2.perror函数

接下来我们也可以了解下 perror 函数,perror函数的使用更加简单方便,可以直接将错误信息打印出来

perror函数的参数里可以写上可能出现错误的命令的名字,它可以打印参数部分的字符串后,再打印个冒号和个空格,再打印错误信息

#include <stdio.h>
#include <string.h>
#include <errno.h>
int main ()
{FILE * pFile;pFile = fopen ("data.txt","r");if (pFile == NULL)perror("fopen");return 0;
}

在这里插入图片描述

到这里我们一些常见的函数基本介绍完了,大家也可以自行去这个网站学习 cplusplus,下一期我们继续学习内存函数的使用和模拟实现!!!


http://www.ppmy.cn/news/1577072.html

相关文章

【leetcode hot 100 73】矩阵置零

解法一&#xff1a;&#xff08;使用两个标记变量&#xff09;用矩阵的第一行和第一列代替方法一中的两个标记数组&#xff08;col、row[ ]&#xff1a;第几列、行出现0&#xff09;&#xff0c;以达到 O(1) 的额外空间。 这样会导致原数组的第一行和第一列被修改&#xff0c;…

Python实战:argparse模块的详细使用

文章目录 一、基本使用步骤二、参数类型三、常用参数选项四、实例演示4.1 使用 argparse 解析命令行参数4.2 完整的示例程序 五、进阶用法六、总结 argparse 是 Python 标准库中用于解析命令行参数的模块&#xff0c;它使得编写用户友好的命令行界面变得简单易行。以下将详细介…

Electron如何执行Python exe程序

在 Electron 应用中执行打包后的 Python exe 程序&#xff0c;通常可以借助 Node.js 的 child_process 模块来实现。以下为你详细介绍具体的实现步骤和示例代码&#xff1a; 1. 确保 Python 可执行文件路径正确 在使用 child_process 模块执行 Python 可执行文件之前&#xf…

makefile中采用echo进行调试

在 Makefile 中使用 echo 输出变量是调试变量值的常用方法。以下是具体用法及注意事项&#xff1a; 一、基础用法&#xff1a;在规则中输出变量 makefile 定义变量 VAR Hello World 在规则中使用 echo 输出变量 print_var: echo “调试 VAR的值是: $(VAR)” 执行命令&#x…

spring boot项目Linux环境jar包启动shell脚本

spring boot jar启动shell脚本示例 使用场景shell脚本文件示例可能遇见的问题1. 无权限使用chomd命令为文件添加权限 2. Shell脚本的行尾格式不兼容通过 Vim 修改文件格式 使用场景 在linux环境&#xff0c;启动小型spring boot项目时&#xff0c;我们总需要一些操作步骤才能将…

软考架构师笔记-进程管理

1.4 进程管理 进程状态&#xff1a;运行、等待、就绪。阻塞不能直接到运行状态&#xff0c;需要到就绪状态排队。五态图等。进程管理&#xff1a;前驱图临界资源&#xff1a;共享的资源&#xff0c;如打印机等临界区&#xff1a;访问临界资源的那段代码称为临界区信号量&#…

代码托管平台对比分析:Gitee与GitLab

文章目录 代码托管平台对比分析&#xff1a;Gitee与GitLab平台概述GiteeGitLab 功能对比代码托管与版本控制CI/CD 集成项目管理用户体验自托管与云托管安全性价格与成本 功能对比表格优势与劣势Gitee 优势Gitee 劣势GitLab 优势GitLab 劣势 实际使用场景Gitee 适用场景GitLab 适…

AI-Ollama本地大语言模型运行框架与Ollama javascript接入

1.Ollama Ollama 是一个开源的大型语言模型&#xff08;LLM&#xff09;平台&#xff0c;旨在让用户能够轻松地在本地运行、管理和与大型语言模型进行交互。 Ollama 提供了一个简单的方式来加载和使用各种预训练的语言模型&#xff0c;支持文本生成、翻译、代码编写、问答等多种…