【算法】【C语言进阶】C语言字符串操作宝藏级别汇总【超详细的使用解释和模拟实现】

作者: @小小Programmer
这是我的主页:@小小Programmer
在食用这篇博客之前,博主在这里介绍一下其它高质量的编程学习栏目:
数据结构专栏:数据结构 这里包含了博主很多的数据结构学习上的总结,每一篇都是超级用心编写的,有兴趣的伙伴们都支持一下吧!
算法专栏:算法 这里可以说是博主的刷题历程,里面总结了一些经典的力扣上的题目,和算法实现的总结,对考试和竞赛都是很有帮助的!

先赞后看好习惯 打字不容易,这都是很用心做的,希望得到支持你 大家的点赞和支持对于我来说是一种非常重要的动力。看完之后别忘记关注我哦!️️️

本篇为不收藏必后悔系列篇~

除了字符串操作函数,博主之前还输出过一篇有关内存操作函数的详解博客和一篇动态内存管理的宝藏级别总结,跟今天这篇的内容也算是息息相关的博客,同样也是干货满满哦!需要的伙伴可以通过传送门食用~
【内存操作】C语言内存函数介绍以及部分模拟实现【初学者保姆级福利】超详细的解释和注释

【动态内存】C语言动态内存管理及使用总结篇【初学者保姆级福利】

本篇博客所介绍的函数在使用时需包含#include<string.h>的头文件。
当然,如果我们需要用assert()来断言指针的话,还要包含#include<assert.h>的头文件。

strlen函数

函数原型:size_t strlen ( const char * str );
作用:求字符串长度(以'\0'为字符串结束标志)。

使用举例:

int main() {
char arr[] = "abcdef";
//char arr[] = { 'a','b','c','d','e','f' };
//如果这样初始化,没有'\0'求出来的结果就是随机值
int len = strlen(arr);
printf("%d\n", len);
return 0;
}

要注意的点:strlen函数的返回值时size_t类型,如果不注意的话容易写出bug。 比如:

int main() {
if (strlen("abc") - strlen("qwertyu") > 0) {
printf(">\n");
}
else {
printf("<\n");
} return 0;
}

此时会打印>,因为两个size_t做减法小于0时,得到的是一个很大的正整数,所以会打印>,这里涉及到整型在内存中的存储问题,我们使用的时候稍微注意一下就行了。

strlen函数的模拟实现

  • strlen的实现方法有很多,有计数器法,有递归法,有指针减指针的方法,这里博主给出的时计数器法。这几种方法的思路都比较简单,这里就不一一展示。
size_t my_strlen(const char* str) {
assert(str);
int count = 0;
while (*str != '\0') {//如果没遇到'\0'说明字符串还没到结尾
count++;
str++;//指针++,找尾,没找到->count++
}
return count;//最后返回计数器结果就可以了,思路很简单
}
int main() {
char arr[] = "abcdef";
int len = my_strlen(arr);
printf("%d\n", len);
return 0;
}

strcpy函数

函数原型:char * strcpy ( char * destination, const char * source );
作用:字符串拷贝,将source指向的字符串内容拷贝到destination所指向的字符串中。

使用举例:

int main() {
char arr1[20] = { 0 };
char arr2[] = "abcdef";
strcpy(arr1, arr2);
printf("%s\n", arr1);//这里打印的就是abcdef
return 0;
}

要注意的点:

  • 注意,原字符串必须以'\0'结尾
  • 拷贝的时候会把原字符串的'\0'也拷贝过去
  • 目标空间一定要足够大
  • 目标空间必须可变(不能传常量字符串的指针过去)

strcpy函数的模拟实现

//如果只是拷贝的话是不需要返回值的,但是如果返回一个dest的起始位置,就可以实现函数的链式访问了。
//src的内容不需要改,所以可以加const,使函数更安全
char* my_strcpy(char* dest, const char* src) {
assert(dest && src);
char* ret = dest;
while (*dest++ = *src++) {//这种拷贝方式是会把'\0'也拷贝过去的,
//这样就和库里面一样了
;
}
return ret;
}
int main() {
char arr1[20] ="xxxxxxxxxxxxxxxxx";
char arr2[] = "abcdef";
strcpy(arr1, arr2);
printf("%s\n", arr1);
return 0;
}

strcat函数

函数原型:char * strcat ( char * destination, const char * source );
作用:字符串追加,将source指向字符串的内容追加到destination所指向的字符串的内容的后面。

使用举例:

int main() {
char arr1[20] = "hello ";
char arr2[] = "bit";
strcat(arr1, arr2);
printf("%s\n", arr1);//这里打印的就是hello bit了
return 0;
}

要注意的点:

  1. 原字符串要有'\0',目标空间也要'\0',否则找不到开始追加的标志
    否则它也会继续往后越界访问,知道找到'\0'为止

strcat函数的模拟实现

追加分为两步:1.找到dest的结尾 2.追加过去(这个过程就和strcpy一样了) 思路都很简单

char* my_strcat(char* dest, const char* src) {
assert(dest && src);
//1.找原字符串中的\0
char* start = dest;
while (*start) {
start++;
}
//开始拷贝
while (*start++ = *src++) {
;
}
return dest;
}
int main() {
char arr1[20] = "hello ";
char arr2[] = "bit";
//my_strcat(arr1, arr2);
printf("%s\n", my_strcat(arr1, arr2));//hello bit
return 0;
}

strcmp函数

函数原型:int strcmp ( const char * str1, const char * str2 );
作用:比较两个字符串(注意:这里比较的不是长度,而是相对应位置的所对应的值的大小)。
返回值: 如果str1大于str2,返回一个大于0的数字,如果相等返回0,如果小于返回一个小于0的数。

使用举例:

int main() {
char arr1[] = "abcdef";
char arr2[] = "abq";
int ret = strcmp(arr1, arr2);
printf("%d\n", ret);//ret是一个小于0的数字
return 0;
}

strcmp函数的模拟实现

思路:通过两个指针,一个指向str1,一个指向str2,一步一步向后比较,发现不一样的就停止比较,如果都同时到达‘\0’,说明两个字符串相等。

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() {
char arr1[] = "abcdef";
char arr2[] = "abq";
//int ret = strcmp(arr1, arr2);
int ret = my_strcmp(arr1, arr2);
printf("%d\n", ret);
return 0;
}

strncpy,strncmp和strncat函数

以上介绍的都是长度不受限制的字符串操作函数,下面要介绍的这三个,都是长度受限的字符串操作函数,使用方法几乎和上面那几个完全一样,只是多了一个参数,仅此而已。

  • 通过按f10或者f11调试,我们就可以很好的了解函数具体是如何工作的,这里不赘述给大家了。

第三个参数: 要拷贝的长度,要比较的长度,要追加的长度。
使用举例:
strncpy:

int main() {
char arr1[] = "abcdef";
char arr2[] = "qwertyuiop";
strncpy(arr1, arr2, 3);
printf("%s\n", arr1);//qwedef
return 0;
}

strncat:

int main() {
//char arr1[20] = "abcdef";
char arr1[20] = "abcdef\0xxxxxxxx";
char arr2[] = "qwertyuiop";
strncat(arr1, arr2, 5);
printf("%s\n", arr1);//abcdefqwert
return 0;
}
//追加结束之后,还会主动放一个\0进去

strncmp:

int main() {
char arr1[] = "abcdef";
char arr2[] = "abcdefqwert";
int ret = strncmp(arr1, arr2, 4);
printf("%d\n", ret);//0
return 0;
}

strstr函数

函数原型:char * strstr ( const char *, const char * );
作用:判断一个字符串是不是另一个字符串的字串
找到了-返回字串在里面出现的地址
找不到-返回空

使用举例:

int main() {
char arr1[] = "abcdefg";
char arr2[] = "cdef";
char* ret = strstr(arr1, arr2);
if (ret == NULL) { printf("找不到子串\n"); }
else {
printf("%s\n", ret);//cdefg
}
return 0;
}

strstr函数的模拟实现

  • 其实,这个函数的模拟实现就是一个经典的字符串找字串问题,这道题所蕴含的思想远不止解决这一个问题。在这里博主提供的是双指针算法的解法,当然,效率更高的还有KMP算法等方法,但是这些算法对于初学的我们来说算比较困难的,有兴趣的伙伴可以自己学习一下KMP算法。

双指针解法: 一个指针跟随,记录开始匹配的位置,另一个指针用于一个一个匹配。
一共要的定义三个指针。

char* my_strstr(const char* str1, const char* str2) {
assert(str1 && str2);
const char* s1 = str1;
const char* s2 = str2;
const char* cur = str1;
while (*cur) {
s1 = cur;
s2 = str2;//这次匹配不成功,回到原位重新开始匹配
while (*s1 && *s2 && *s1 == *s2) {
s1++;
s2++;
}
if (*s2 == '\0') {//这次匹配成功了
return (char*)cur;
}
cur++;//匹配失败,说明从cur开始匹配不行
}
return NULL;
}
//测试案例
int main() {
char arr1[] = "abcdefg";
char arr2[] = "cdef";
char* ret = my_strstr(arr1, arr2);
if (ret == NULL) { printf("找不到子串\n"); }
else {
printf("%s\n", ret);
}
return 0;
}

strtok函数

函数原型:char * strtok ( char * str, const char * sep);
作用:切割一个字符串,str是待操作的字符串,sep是切割符号的集合。

  • sep参数是个字符串,定义了用作分隔符的字符集合
    第一个参数指定一个字符串,它包含了0个或多个由sep字符串中一个或多个分隔符分割的标记

  • strtok函数找到str中的下一个标记,并将其用'\0'结尾,返回一个指向这个标记的指针
    (注:strtok函数会改变被操作的字符串,所以在使用的时候一般都是临时拷贝的内容)

  • strtok函数的第一个参数不为NULL时,函数将找到str中的第一个标记,strtok函数将保存它在字符串中的位置

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

  • 如果字符串中不存在更多的标记,则返回NULL

看起来比较复杂,我们看一个例子就可以很好的理解了:

int main() {
char arr[] = "xiaoxiaoprogrammer@yeah.net";
char buf[30] = { 0 };
strcpy(buf, arr);
const char* sep = "@.";
//"@."--sep
printf("%s\n",strtok(buf, sep));//只找到第一个标记
printf("%s\n",strtok(NULL, sep));//从保存好的位置继续往后找
printf("%s\n", strtok(NULL, sep));//从保存好的位置继续往后找
return 0;
}


当然,这样写的代码就非常刻板,因为我们提前知道了要分割成3份,所以我们还可以优化。

  • 我们发现传buf只需要传一次,所以巧妙利用for循环,for循环初始化只执行一次
int main() {
char arr[] = "xiaoxiaoprogrammer@yeah.net";
char buf[30] = { 0 };
strcpy(buf, arr);
const char* sep = "@.";
//"@."--sep
//我们发现传buf只需要传一次,所以巧妙利用for循环,for循环初始化只执行一次
char* str = NULL;
//巧妙利用for循环
for (str = strtok(buf, sep); str != NULL; str = strtok(NULL, sep)) {//处理完要赋值
printf("%s\n", str);
}
return 0;
}

至于这个函数该如何实现,我们就先不关心了,有兴趣的伙伴可以自己尝试一下,至于为什么每次用完strtok,不用传str和切割位置过去,它还记得上次切割完的位置在哪里?我们不需要深入了解,但是,我们知道的是,它里面肯定包含了一个静态的指针变量,在strtok调用完之后,还记录着上一次切割的位置的地址。

尾声

以上就是今天这篇博客的全部内容了。如果还对内存函数,动态内存处理,算法或者数据结构的伙伴,可以到文章开头的传送门食用。如果你感觉这篇博客对你有帮助的话,不要忘了一键三连,点赞收藏关注再离开噢!

【算法】【C语言进阶】C语言字符串操作宝藏级别汇总 strtok函数 strstr函数该怎么用?【超详细的使用解释和模拟实现】的更多相关文章

  1. [C语言]进阶|指针与字符串

    ------------------------------------------------------------------------------------ 回顾:[C语言]指针与字符串 ...

  2. C语言中常用的字符串操作函数

    程序开头要声明 #include <string.h> 函数名: stpcpy 功 能: 拷贝一个字符串到另一个 用 法: char *stpcpy(char *destin, char ...

  3. go内建容器-字符和字符串操作

    1.基础定义 在基础语法篇提到过golang的rune相当于其他编程语言的char,其本质是一个int32(四字节),用[]rune来转换一个字符串时,得到的是个解码后的结果,存储在新开辟的[]run ...

  4. PHP开发中常用的字符串操作函数

    1,拼接字符串 拼接字符串是最常用到的字符串操作之一,在PHP中支持三种方式对字符串进行拼接操作,分别是圆点.分隔符{}操作,还有圆点等号.=来进行操作,圆点等号可以把一个比较长的字符串分解为几行进行 ...

  5. go语言之进阶篇字符串操作常用函数介绍

    下面这些函数来自于strings包,这里介绍一些我平常经常用到的函数,更详细的请参考官方的文档. 一.字符串操作常用函数介绍 1.Contains func Contains(s, substr st ...

  6. C语言字符串操作总结大全(超详细)

    本篇文章是对C语言字符串操作进行了详细的总结分析,需要的朋友参考下 1)字符串操作  strcpy(p, p1) 复制字符串  strncpy(p, p1, n) 复制指定长度字符串  strcat( ...

  7. 零基础学习C语言字符串操作总结大全

    本篇文章是对C语言字符串操作进行了详细的总结分析,需要的朋友参考下 1)字符串操作 strcpy(p, p1) 复制字符串 strncpy(p, p1, n) 复制指定长度字符串 strcat(p, ...

  8. C语言字符串操作常用库函数

    C语言字符串操作常用库函数 *********************************************************************************** 函数 ...

  9. c语言字符串操作大全

     C语言字符串操作函数 函数名: strcpy 功  能: 拷贝一个字符串到另一个 用  法: char *stpcpy(char *destin, char *source); 程序例: #incl ...

  10. 转:C语言字符串操作函数 - strcpy、strcmp、strcat、反转、回文

    转自:C语言字符串操作函数 - strcpy.strcmp.strcat.反转.回文 C++常用库函数atoi,itoa,strcpy,strcmp的实现 作者:jcsu C语言字符串操作函数 1. ...

随机推荐

  1. 20级训练赛Round #5

    A - 凯少与素数 签到 & 思维题, 要使每一对数字 \((i,j)\) 的最大公约数都等于 1,简单来说区间相邻的两个数一定 \(gcd(i,j) = 1\) 并且 \((r - l)\) ...

  2. 体验函数计算 FC 3.0,写测评赢取索尼头戴式耳机

    11月1日云栖大会,函数计算3.0全新升级,相对函数计算2.0,3.0版本突出易用性.高弹性,并且可以和更多阿里云服务无缝集成.业内首发神龙 Serverless GPU 架构,冷启动大幅优化,全链路 ...

  3. 如何用 Serverless 一键部署 Stable Diffusion?

    思路 其实很简单, 我们只需要将镜像里面的动态路径映射到 NAS文件存储里面即可,利用 NAS 独立存储文件模型,扩展,语言包等,并且我们可以为管理 NAS 单独配置一个可视化的后台,用简单的文件上传 ...

  4. hbulider 运行微信开发者工具 Enable IDE Service (y/N) 

    问题:hbulider 运行微信开发者工具 出现如下页面,没有输入y的交互界面  解决办法:先安装微信开发者工具,然后打开微信开发者工具 再次运行:

  5. 彻底解决 gcr、quay、DockerHub 镜像下载难题

    在使用 Docker 和 Kubernetes 时,我们经常需要访问 gcr.io 和 quay.io 镜像仓库,由于众所周知的原因,这些镜像仓库在中国都无法访问,唯一能访问的是 Docker Hub ...

  6. Metastability 亚稳态问题

    亚稳态问题 各种跨时钟域的问题都会归结于亚稳态的问题,IP设计时钟域不超过两个,对于CDC设计要求不高;对于SoC设计来说,CDC处理十分重要 1.什么是亚稳态? transition time 是可 ...

  7. 【MicroPython】生成QSTR表 - py\makeqstrdata.py

    转义非字母数字的字符,转义结果为预定义字符串codepoint2name[] def qstr_escape(qst): def esc_char(m): c = ord(m.group(0)) tr ...

  8. 【rt-thread】SConscript文件添加格式必须是4空格开头

    SConscript文件添加格式必须是4空格开头,TAB或其他数量的空格均错误

  9. Shell-表达式-比较-文件判断-权限判断-条件-逻辑

  10. [转帖]centos6 free 和 centos 7的free 的差异与对比

    目录 一 centos6 free 常用参数和含义 centos6 free 命令示例 free 值讲解 计算公式 二 centos7 free 常用的参数 centos7 free 命令示例 计算公 ...