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

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

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

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

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

#includeassert()#include

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;
}
strlensize_t
int main() {
if (strlen("abc") - strlen("qwertyu") > 0) {
printf(">\n");
}
else {
printf("<\n");
} return 0;
}
>size_t>

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 );

使用举例:

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 );

使用举例:

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

要注意的点:

'\0''\0''\0'

strcat函数的模拟实现

dest
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 );str1str2

使用举例:

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);strsep
sepsepstrtok'\0'NULLstrstrtokNULLNULL

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

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调用完之后,还记录着上一次切割的位置的地址。

尾声

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