【算法】【C语言进阶】C语言字符串操作宝藏级别汇总 strtok函数 strstr函数该怎么用?【超详细的使用解释和模拟实现】
【算法】【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;
}
要注意的点:
- 原字符串要有
'\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函数该怎么用?【超详细的使用解释和模拟实现】的更多相关文章
- [C语言]进阶|指针与字符串
------------------------------------------------------------------------------------ 回顾:[C语言]指针与字符串 ...
- C语言中常用的字符串操作函数
程序开头要声明 #include <string.h> 函数名: stpcpy 功 能: 拷贝一个字符串到另一个 用 法: char *stpcpy(char *destin, char ...
- go内建容器-字符和字符串操作
1.基础定义 在基础语法篇提到过golang的rune相当于其他编程语言的char,其本质是一个int32(四字节),用[]rune来转换一个字符串时,得到的是个解码后的结果,存储在新开辟的[]run ...
- PHP开发中常用的字符串操作函数
1,拼接字符串 拼接字符串是最常用到的字符串操作之一,在PHP中支持三种方式对字符串进行拼接操作,分别是圆点.分隔符{}操作,还有圆点等号.=来进行操作,圆点等号可以把一个比较长的字符串分解为几行进行 ...
- go语言之进阶篇字符串操作常用函数介绍
下面这些函数来自于strings包,这里介绍一些我平常经常用到的函数,更详细的请参考官方的文档. 一.字符串操作常用函数介绍 1.Contains func Contains(s, substr st ...
- C语言字符串操作总结大全(超详细)
本篇文章是对C语言字符串操作进行了详细的总结分析,需要的朋友参考下 1)字符串操作 strcpy(p, p1) 复制字符串 strncpy(p, p1, n) 复制指定长度字符串 strcat( ...
- 零基础学习C语言字符串操作总结大全
本篇文章是对C语言字符串操作进行了详细的总结分析,需要的朋友参考下 1)字符串操作 strcpy(p, p1) 复制字符串 strncpy(p, p1, n) 复制指定长度字符串 strcat(p, ...
- C语言字符串操作常用库函数
C语言字符串操作常用库函数 *********************************************************************************** 函数 ...
- c语言字符串操作大全
C语言字符串操作函数 函数名: strcpy 功 能: 拷贝一个字符串到另一个 用 法: char *stpcpy(char *destin, char *source); 程序例: #incl ...
- 转:C语言字符串操作函数 - strcpy、strcmp、strcat、反转、回文
转自:C语言字符串操作函数 - strcpy.strcmp.strcat.反转.回文 C++常用库函数atoi,itoa,strcpy,strcmp的实现 作者:jcsu C语言字符串操作函数 1. ...
随机推荐
- HTML+CSS小实战案例 (照片墙特效、代码展示)
预览图: HMTL代码部分 <!DOCTYPE html> <html lang="en"> <head> <meta charset=& ...
- linux ntp时间服务器搭建
工作中经验遇到搭建时间服务器的任务,如何搭建网上找的例子总是有些许问题,如下自己动手操作一遍总结一下,方便自己和后来人直接上手使用. 准备工作:192.168.0.1 服务端: ntp服务器192 ...
- <vue 基础知识 9、v-model使用 input、radio、checkbox、select、修饰符>
代码结构 一. 01-v-model的基本使用 Vue中使用v-model指令来实现表单元素和数据的双向绑定 1.效果 2.代码 01-v-model的基本使用.html <!DOCTY ...
- java进阶(12)--8种数据包装类型、Integer、常用方法
一.基本数据类型与包装类型 8种基本数据类型,对应的包装类,父类 1.byte-->java.lang.Byte-->Number 2.short-->java.lang.Short ...
- 07-逻辑仿真工具VCS-Post processing with VCD+ files
逻辑仿真工具-VCS 编译完成不会产生波形,仿真完成之后,生成波形文件,通过dve产看波形 vcd是波形文件的格式,但是所占的内存比较大,后面出现了vpd(VCD+)波形文件 将一些系统函数嵌入到源代 ...
- Mygin实现动态路由
本篇是Mygin的第四篇 目的 使用 Trie 树实现动态路由解析. 参数绑定 前缀树 本篇比前几篇要复杂一点,原来的路由是用map实现,索引非常高效,但是有一个弊端,键值对的存储的方式,只能用来索引 ...
- [转帖]Oracle 12.2 新特性 | PDB不同字符集变更
https://www.cnblogs.com/cqdba/p/8bef7c432b87807c0680d6791f427b09.html 在oracle12.1版本中,同一CDB中的所有PDB使用的 ...
- Oracle12c新增max_idle_time参数的学习与感触
Oracle12c新增max_idle_time参数的学习与感触 TLDR 其实任何软件出了新版本.readme 是很重要的. 尤其是数据库, 涉及到底层问题的. 比如这次遇到的Oracle的max_ ...
- [转帖]一次python服务的性能优化经历
https://juejin.cn/post/7208708762265616421 问题背景: 在我们的业务中,有一些推荐的场景会需要走到集团研究院的算法推荐服务,对一些用户进行个性化的课件推荐 ...
- [转帖]新一代垃圾回收器ZGC的探索与实践
1. 引入 1.1 GC之痛 很多低延迟高可用Java服务的系统可用性经常受GC停顿的困扰. GC停顿指垃圾回收期间STW(Stop The World),当STW时,所有应用线程停止活动,等待GC停 ...