【算法】【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. AcWing 第 13 场周赛  补题记录

    比赛链接:Here AcWing 3811. 排列 签到题, 先输出 \(n\) 然后输出 \(1\sim n -1\) 即可 AcWing 3812. 机器人走迷宫 不会什么特别高级的方法 qaq, ...

  2. Intellij IDEA安装与配置教程(Windows版)

    Intellij IDEA(简称IDEA)是Java语言的集成开发环境,在业界公认为是一款优秀的Java开发工具.分为Community社区版(免费)和Untimate终极版(付费). IDEA是一款 ...

  3. windows 系统关闭占用端口的应用

    开发中有时候开发工具把程序关闭了,但是后台并没有真正关闭程序,导致再次启动相同端口的程序时报端口已经被使用的错误,这时如何强制关闭已占用的端口 1.打开dos对话框 2.查找被占用的端口的进程号 ne ...

  4. 2020年了,再不会webpack敲得代码就不香了(近万字实战)

    https://zhuanlan.zhihu.com/p/99959392?utm_source=wechat_session&utm_medium=social&utm_oi=619 ...

  5. C# 用树形列表 动态 显示菜单

    如图 页面加载 private void Form1_Load(object sender, EventArgs e) { GetMenu(treeView1, menuStrip1);//将menu ...

  6. influxdb 端点使用http进行sql查询,写数据

    转载请注明出处: InfluxDB有以下几个常用的端点,它们的作用和传参方式如下: 1./ping 端点: 作用:用于检查InfluxDB实例的状态,返回InfluxDB的构建类型和版本信息. 传参: ...

  7. AvaloniaUI 取消标题栏,无边框无最大最小化,

    AvaloniaUI 取消标题栏,无边框无最大最小化, 创建一个Window控件 并且在Window中添加以下代码 ExtendClientAreaToDecorationsHint="Tr ...

  8. 当ChatGPT遇上了CoT

    最近在看CoT(Chain-of-Thought,思维链)方面的论文<Chain-of-Thought Prompting Elicits Reasoning in Large Language ...

  9. Laravel - blade 模板继承的使用

    1. 模板文件 <!DOCTYPE html> <html lang="en"> <head>     <meta charset=&qu ...

  10. Linux-分区-fdisk-mkfs-mount