今天向大家整理了我们经常会使用的字符串函数的使用和实现,文章能够更好的帮助我们理解字符串函数的使用,干货很多,希望大家多多支持。
1 求字符串长度
1-1 strlen
相信大家对strlen的使用已经非常了解,这里就不在多说,我们来看看strlen的实现:
method 1:计数器
method 1:计数器 int MyStrlen1(char* str) { assert(str); int count=0; while (*str != '\0') { count++; str++; } return count; }
method 2:递归
method 2:递归 int MyStrlen2(char* str) { if (*str != '\0') return MyStrlen2(str + 1)+1; else return 0;
method 3:指针-指针
method 3:指针-指针 int MyStrlen3(char* str) { char* start = str; char* end = str; while (*end != '\0') end++; return end - start; }
这3种方法的实现比较简单,本文重点不在这里,只是帮助大家简单回忆一下。接下来我们来看一看下面这道题:
#include <stdio.h> int main() { const char*str1 = "abcdef"; const char*str2 = "bbb"; if(strlen(str2)-strlen(str1)>0) { printf("str2>str1\n"); } else { printf("srt1>str2\n"); } return 0; }
这道题的输出结果是什么?如果大家是第一次做这种题,很容易掉入陷阱,应该都会选择str1>str2.我们要了解一下strlen的返回类型是什么?
size_t strlen ( const char * str );
size_t 表示一种无符号数,当两个无符号数相减时得到的数肯定还是一个无符号数,既然是无符号数得到的结果肯定是>0 的。
2 长度不受限制的字符串函数
2-1 strcpy
我们简单来看看该函数使用的一些小规则:
char* strcpy(char * destination, const char * source );
- 源字符串必须以 '\0' 结束。
- 会将源字符串中的 '\0' 拷贝到目标空间。
- 目标空间必须足够大,以确保能存放源字符串。
- 目标空间必须可变。
自己实现strcpy函数:
char* MyStrcpy2(char* des, const char* src) { char* ret = des; assert(des); assert(src); while (*des++ = *src++) { ; } return ret;
实现的方法很简单,这里就不再多说。
2-2 strcat
char * strcat ( char * destination, const char * source );
- 源字符串必须以 '\0' 结束。
- 目标空间必须有足够的大,能容纳下源字符串的内容。
- 目标空间必须可修改
自己实现strcat函数:
自己实现strcat char* MyStrcat(char* des, const char* src) { char* ret = des; assert(des); assert(src); while (*des != '\0') { des++; } while (*des++ = *src++) { ; } return ret; }
其实方法与实现strcpy差不多,只不过要先将des指向'\0'后再追加。那么这里提一个小问题:该函数能自己追加自己吗?
显然是不能够的。因为会把自身的'\0'给替换掉,从而导致一些错误。解决方法是使用strncat函数来解决,下面会详细的讲解。
2-3 strcmp
int strcmp ( const char * str1, const char * str2 );
标准规定:
- 第一个字符串大于第二个字符串,则返回大于0的数字
- 第一个字符串等于第二个字符串,则返回0
- 第一个字符串小于第二个字符串,则返回小于0的数字
每个编译器可能返回的数值不尽相同,但是都遵循c标准,我们来看看在vs下自己实现strcmp:
int MyStrcmp(const char* str1, const char* str2) { assert(str1); assert(str2); while (*str1 == *str2) { if (*str1 == '\0') return 1; str1++; str2++; } if (*str1 > *str2) return 1; else return -1; }
3 长度受限制的字符串函数
3-1 strncpy
char * strncpy ( char * destination, const char * source, size_t num );
- 拷贝num个字符从源字符串到目标空间。
- 如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个
自己实现strncpy:
自己实现strncpy char* MyStrncpy(char* des, const char* src, int count) { assert(des); assert(src); char* begin = des; while (count && (*des++ = *src++))//当count=0或者*src='\0'时都会跳出循环 { count--; } if (count)// count!=0时,会在目标数组后加上'\0' 直到count=0 { while (--count) *des++ = '\0'; } return begin;
这里面值得注意的是当*src='\0',但是count !=0时会在des后面加'\0',直至count=0.
3-2 strncat
char * strncat ( char * destination, const char * source, size_t num
自己实现strncat:
自己实现strncat char* Mystrcat(char* des, const char* src, int count) { char* start = des; assert(des); assert(src); while (*des++) { ; } des--; while (count--) { if (!(*des++ = *src++))//如果在来源数组找到了'\0'就直接返回start return start; } *des = '\0';//在来源数组没有发现'\0',在后面添加一个'\0' return start; }
其实与strcpy和strcat一样,strncpy和strncat实现方法有很多类似的地方,这里除了将des先指向'\0'后,还有一个值得注意的地方:如果在来源数组中没有找到'\0'并且count !='\0',会直接在des后面加上一个'\0',不会等到count=0才结束加'\0'.
3-3 strncmp
int strncmp ( const char * str1, const char * str2, size_t num );
自己实现strncmp int MyStrncmp(const char* str1, const char* str2,int count) { assert(str1); assert(str2); int ret = 0; while (!(ret = (*str1 - *str2)) && *str1 && --count) { str1++; str2++; } if (ret > 0) return 1; else if (ret < 0) return -1; else return 0; }
这里的实现方法与strcmp类似,只不过多了一个count来控制要比较字符的个数。
4 字符串查找函数
4-1 strstr
char * strstr ( const char *str1, const char * str2);
自己实现strstr函数:
自己实现strstr char* MyStrstr(const char* str1,const char* str2) { assert(str1); assert(str2); char* cur = (char*)str1; if (*str2 == '\0') return (char*)str1; while (*cur) { char* p1 = cur; char* p2 = (char*)str2; while (*p1 && *p2 && *p1 == *p2) { p1++; p2++; } if (*p2 == '\0') return cur; if (*p1 == '\0')//当str1都找完了后还没有找到,就返回NULL,这一步可以省略(加上后某些情况效率更高) return NULL; cur++; } return NULL; } int main() { char arr1[] = "abbbcd"; char arr2[] = "bbc"; char* ret = MyStrstr(arr1, arr2); if (ret == NULL) printf("没找到\n"); else printf("找到了:%s\n", ret); return 0; }
这个函数较前面函数的实现会稍微复杂一些,但整体来看还是比较好理解:这个函数实现逻辑是这样的:给定两个字符数组,要在其中一个数组中查找另一个数组中所有元素是否为该数组的子集,如果查找成功,就返回第一个相同元素的地址,没有查到就返回NULL.具体细节可以参考上面的代码。
4-2 strtok
char * strtok ( char * str, const char * sep );
- sep参数是个字符串,定义了用作分隔符的字符集合 。
- 第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标记。
- strtok函数找到str中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针。(注: strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容 并且可修改。)
- strtok函数的第一个参数不为 NULL ,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置。
- strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标记。
- 如果字符串中不存在更多的标记,则返回 NULL 指针。
strtok的使用:
strtok的使用 int main() { char arr1[] = "grm@3405446829.com"; char arr2[] = "@."; //不推荐这样写,比较麻烦 char* ret = strtok(arr1, arr2); printf("%s\n", ret); ret = strtok(NULL, arr2); printf("%s\n", ret); ret = strtok(NULL, arr2); printf("%s\n", ret);
这样写的话代码显得很冗余,并且比较麻烦,下面这种方法用for循环来处理就比较合理:
for (char* ret = strtok(arr1, arr2); ret != NULL; ret = strtok(NULL, arr2)) { printf("%s\n", ret); }
这样写代码会使效率高上不少。
5 错误信息报告函数
5-1 strerror
char * strerror ( int errnum );
//strerror的使用 #include<errno.h> int main() { FILE* pf = fopen("text.txt", "r"); if (pf == NULL) { printf("%s\n", strerror(errno)); } else printf("open file success\n"); return 0; }
这个函数一般使用的比较少,但是我们也要基本了解它的用法.
好了,今天的分享就到这里了,如果哪里有什么不对的地方,希望各位大佬帮忙指正一下。