当前位置:   article > 正文

字符串函数(超详细)

字符串函数

       C语言中有很多库函数,其中我们有必须知道一些函数的使用,其中比较典型是字符串函数,内存函数,和动态内存函数。这篇文章我们就来了解字符串函数。使用字符串函数要引用string.h头文件。

目录

字符串函数

1.strlen函数

2.strcpy函数

​编辑3.strcat函数

4.strcmp函数

5.strncat函数

6.strncpy函数 

 7.strncmp函数

8.strstr函数 

9.strtok函数 

10.strerror函数

11.其他字符传函数 

字符串函数

1.strlen函数

       这是我们最熟悉的一个求字符串长度的函数。我们先看C语言里面它的标准声明。       C定义size_t是无符号整数(unsigned int),我们会找字符串结束的标志'\0',之后返回无符号整数。我们来模拟实现这个函数。

  1. //size_t == unsigned int
  2. int my_strlen(const char* str)
  3. {//const修饰指针里面的变量,指针指向的内容不能通过解引用改变
  4. int count = 0;
  5. assert(str != NULL);
  6. while (*str != 0)//==while(*str)
  7. {
  8. count++;
  9. str++;
  10. }
  11. return count;
  12. }
  13. int main()
  14. {
  15. //int len = my_strlen("abcdef");
  16. //printf("%d\n", len);
  17. // 3 - 6=-3(无符号数运算结果还是无符号数)
  18. if (strlen("abc") - strlen("abcdef") > 0)
  19. {//strlen返回类型是unsignde int
  20. printf("hehe\n");
  21. }
  22. else
  23. {
  24. printf("haha\n");
  25. }
  26. return 0;
  27. }

       这里打印的是“hehe”因为无符号运算无论如何结果都是大于0的,所以打印hehe.

       我们还可以通过递归的方法来实现:

  1. int my_strlen(const char* str)
  2. {
  3. if (*str == '\0')
  4. {
  5. return 0;
  6. }
  7. else
  8. {
  9. return 1 + my_strlen(str + 1);
  10. }
  11. }
  12. int main()
  13. {
  14. //递归的方式实现 strlen
  15. char arr[] = "abcd";
  16. size_t len = my_strlen(arr);
  17. printf("len = %zd\n", len);
  18. return 0;
  19. }

       当读到最后一个字符‘\0’时返回0。这里不再做过多解释。

2.strcpy函数

       拷贝字符串函数,先传入要改变的字符串,再传入要拷贝的内容。返回的值是改变字符串的首元素地址。一定牢记,字符串结束的标志是'\0'。所以只需要将复制的字符串拷贝到'\0'即可,后面即使没有变也不需要读取。

       我们先来看标准定义:

  1. int main()
  2. {
  3. char arr1[20] = { 0 };
  4. char arr2[] = "hello";
  5. strcpy(arr1, arr2);
  6. printf("%s\n", arr1);
  7. return 0;
  8. }

       目标空间必须确保足够大,能放的下拷贝的字符串。但是有一种情况,就是注意指针可能指向常量字符串,这样空间是不能被修改的。       接下来我们模拟实现strcpy.

  1. char* my_strcpy(char* dest,const char* src)
  2. {//使src是常量字符串
  3. assert(dest != NULL);
  4. assert(src != NULL);
  5. char* ret = dest;
  6. //ret指向dest的首位置
  7. //while (*src)
  8. //{
  9. // *dest++ = *src++;
  10. //}
  11. //*dest = *src;
  12. while (*dest++ = *src++)
  13. {//最后直接把'\0'赋给dest
  14. ;
  15. }
  16. //返回目的空间的起始地址
  17. return ret;
  18. }
  19. int main()
  20. {
  21. //错误示范
  22. //char*p="abcdef"是常量字符串内容不可改
  23. //arr1[]="ab"会崩
  24. char arr1[] = "abcdefghi";
  25. char arr2[] = "bit";
  26. my_strcpy(arr1, arr2);
  27. printf("%s\n", arr1);
  28. return 0;
  29. }

3.strcat函数

       追加字符串函数,先传入在前面的字符串,再传入在后面的字符串,返回的是最前面元素的首地址。       一样的,也是要确保目标字符串内存够大。我们先来看看目标空间不足的情况:

  1. int main()
  2. {
  3. char a1[] = "hello";
  4. char a2[] = "world";
  5. strcat(a1, a2);
  6. printf("%s\n", a1);
  7. return 0;
  8. }

       所以空间一定要够。 

  1. int main()
  2. {
  3. char a1[30] = "hello\0xxxxxxx";
  4. char a2[] = "world";
  5. strcat(a1, a2);
  6. printf("%s\n", a1);
  7. return 0;
  8. }

        使用库函数strcat不要给自己追加,否则会崩溃。       

     那这个情况该如何解决?放心,C语言考虑的很全面,会有办法,我们一会再讲。  

     我们再来模拟实现一下: 

  1. char*my_strcat(char* dest, const char* src)
  2. {
  3. assert(dest && src);
  4. char* ret = dest;
  5. //1.找到目的字符串的'\0'
  6. while (*dest != '\0')
  7. {
  8. dest++;
  9. }
  10. //2.追加
  11. while (*dest++ = *src++)
  12. {
  13. ;
  14. }
  15. return ret;
  16. }
  17. int main()
  18. {
  19. char a1[30] = "hello";
  20. char a2[] = "world";
  21. my_strcat(a1, a2);
  22. printf("%s\n", a1);
  23. return 0;
  24. }

       先在原字符串找到‘\0’,之后令它们++相等。

4.strcmp函数

       比较字符串函数,从第个开始比较,之后比较下一个,一个一个的比较,直到比较完,如果都相同就返回0;前者>后者返回1(不同编译器返回值不同,可能是两个数的ASCII值的差);前者<后者返回-1(可能是两个值的ASCII值的差)。

  1. int main()
  2. {
  3. //VS 2020
  4. //> 1
  5. //== 0
  6. //< -1
  7. //linux-gcc
  8. //> >0
  9. //== 0
  10. //< <0
  11. char* p1 = "abcdef";
  12. char* p2 = "sqwer";
  13. //strcmp比较的不是不是字符串长度
  14. //一对一对往后比较,不一样的话直接返回
  15. //int ret = strcmp(p1, p2);
  16. if (strcmp(p1, p2) >0)
  17. {
  18. printf("p1>p2\n");
  19. }
  20. else if (strcmp(p1, p2) == 0)
  21. {
  22. printf("p1=p2\n");
  23. }
  24. else if (strcmp(p1, p2) <0)
  25. {
  26. printf("p1<p2\n");
  27. }
  28. //printf("%d\n", ret);
  29. return 0;
  30. }

       我们依旧来模拟实现一下:

  1. int my_strcmp(const char* s1, const char* s2)
  2. {
  3. while (*s1 == *s2)
  4. {
  5. if (*s1 == '\0')
  6. {
  7. return 0;
  8. }
  9. s1++;
  10. s2++;
  11. }
  12. return *s1 - *s2;
  13. }
  14. int main()
  15. {
  16. int ret = my_strcmp("bbq", "abcdef");
  17. if (ret > 0)
  18. {
  19. printf("大于\n");
  20. }
  21. else if (ret == 0)
  22. {
  23. printf("等于\n");
  24. }
  25. else
  26. {
  27. printf("小于\n");
  28. }
  29. return 0;
  30. }

       这里是几个重要的字符串函数,我们要学会它们的基本用法。

       strcpy、strcat、strcmp长度不受限制的字符串函数。那么刚才strcat无法追加自己,我们总要解决,是不是加上指定追加的长度就可以完成?所以库函数又提供了一些长度受限的字符串函数。

       strncpy、strncat、strncmp长度受限制的字符串函数。

5.strncat函数

        传入在前的字符串,之后传入在后面的字符串,在之后传入要追加的元素个数,返回的是最前面元素的地址。

  1. int main()
  2. {
  3. char arr1[30] = "hello";
  4. char arr2[] = "world";
  5. strncat(arr1, arr2, 8);
  6. //最后拷贝'\0';即使追加个数大于的原字符串长度
  7. //就把最后的元素追加上去,之后补一个'\0'
  8. printf("%s\n", arr1);
  9. return 0;
  10. }

       即使传入的整数>要拷贝的元素个数,最后会用‘\0’来代替。 

       我们来模拟实现strncat。

  1. void my_strncat(char* dest, const char* str, int sz)
  2. {
  3. assert(str);
  4. char* cur = dest;
  5. while (*cur)
  6. {
  7. cur++;
  8. }
  9. int i = 0;
  10. for (i = 0; i < sz; i++)
  11. {
  12. *cur++ = *dest++;
  13. }
  14. }
  15. int main()
  16. {
  17. char arr1[20] = "abcd";
  18. strncat(arr1, arr1, sizeof(char) * 7);
  19. printf("%s\n", arr1);
  20. return 0;
  21. }

6.strncpy函数 

       和strcpy函数很像,多传入一个要拷贝字符的数量。返回的类型是整数,和strcpy返回的规律是一样的。

  1. int main()
  2. {
  3. char arr1[20] = "xxxxxxxxx";
  4. char arr2[] = "hello";
  5. strncpy(arr1, arr2, 7);
  6. printf("%s\n", arr1);
  7. return 0;
  8. }

       我们来模拟一下: 

  1. void my_strncpy(char* dest, const char* str, size_t sz)
  2. {
  3. int i = 0;
  4. char* cur = str;
  5. for (i = 0; i < sz; i++)
  6. {
  7. if (i < strlen(cur))
  8. {
  9. *dest++ = *str++;
  10. }
  11. else
  12. {
  13. *dest++ = '\0';
  14. }
  15. }
  16. }
  17. int main()
  18. {
  19. char arr1[20] = "xxxxxxxxxxx";
  20. char arr2[] = "abcd";
  21. my_strncpy(arr1, arr2, sizeof(char) * 6);
  22. printf("%s\n", arr1);
  23. return 0;
  24. }

 7.strncmp函数

       和strcmp函数很像。返回的类型是整数,和strcmp返回的规律是一样的。

  1. int main()
  2. {
  3. //strncmp - 字符串比较
  4. const char* p1 = "abcdef";//p1指向内容不能修改
  5. const char* p2 = "abcqwer";
  6. //int ret = strcmp(p1, p2);
  7. int ret = strncmp(p1, p2, 3);//比较前n个字符串
  8. printf("%d\n", ret);
  9. return 0;
  10. }

8.strstr函数 

       判断字符串(子串)是否为另一个字符串(父串)的子串。返回类型为指针,如果不是返回NULL(空指针)。可以理解为字符串匹配函数。

  1. int main()
  2. {
  3. //strstr查找子字符串函数
  4. //char* strstr(const char* string,const char* strCharSet);
  5. //NULL -- 空指针
  6. //NUL/Null - '\0'
  7. //string
  8. //Null-terminated string to search 从'\0'开始查找原字符串
  9. //strCharSet
  10. //Null-terminated string to search for 要查找的字符串
  11. char* p1 = "abcdefghi";
  12. char* p2 = "def";
  13. char* ret = strstr(p1, p2);
  14. //如果没找到就返回空指针
  15. //如果找到了返回的是源字符串中找到的比较字符串中首元素的地址
  16. if (ret == NULL)
  17. {
  18. printf("子串不存在\n");
  19. }
  20. else
  21. {
  22. printf("%s\n", ret);
  23. }
  24. return 0;
  25. }

       我们来模拟实现strstr函数,通过暴力求解法BF算法。

  1. //模拟实现strstr
  2. char* my_strstr(const char* p1, const char* p2)
  3. {
  4. assert(p1 && p2);
  5. char* s1 = p1;
  6. char* s2 = p2;
  7. char* cur = p1;
  8. if (*p2 == '\0')
  9. {//如果p2是空字符串
  10. return p1;
  11. }
  12. while (*cur)
  13. {
  14. s1 = cur;//如果碰到两个相同,第三个不同,则下次到第二个相同的开始
  15. //如:abcdddef
  16. // ddef
  17. s2 = p2;//比较的字符串每次从头开始
  18. while ((*s1 != '\0')&&(*s2 != '\0')&&(*s1 == *s2))
  19. {
  20. s1++;
  21. s2++;
  22. }
  23. if (*s2 == '\0')
  24. {
  25. return cur;//找到子串
  26. }
  27. cur++;
  28. }
  29. return NULL;//找不到子串
  30. }
  31. int main()
  32. {
  33. char* p1 = "abcdddefg";
  34. char* p2 = "ddef";
  35. char* ret = my_strstr(p1, p2);
  36. //如果没找到就返回空指针
  37. //如果找到了返回的是源字符串中找到的比较字符串中首元素的地址
  38. if (ret == NULL)
  39. {
  40. printf("子串不存在\n");
  41. }
  42. else
  43. {
  44. printf("%s\n", ret);
  45. }
  46. return 0;
  47. }

        因为指向主串的指针不会回溯,但是指向要匹配的字符串(子串)如果不匹配就要回溯。所以我们多定义两个变量来记录原位置。

       这是最暴力的解决方式,当然有一种高效的KMP算法,以后我会发布博客,大家下去也可以探索探索。

9.strtok函数 

       这个函数很有意思,你可以理解为字符串分割函数。相当于分隔字符串函数,要传入原字符串,之后在创建一个字符串记录原字符串的分隔符,调用一次返回的是原字符串首元素的地址,这个函数会把原字符串中的分隔符改为‘\0’,所以为了不让它修改原字符串,我们先复制一份,在修改。第一次调用后会把分隔符改为‘\0’,下一次要传入一个空指针(NULL),返回的是空指针后面元素的地址。

  1. int main()
  2. {
  3. //192.168.31.121
  4. //192 168 31 121 - strtok
  5. //char* strtok(char* str,char* sep)
  6. //sep参数是个字符串,定义了用作分隔符的字符集合
  7. char arr[] = "zpw@bitedu.tech";
  8. char* p = "@.";
  9. //strtok(arr,p);
  10. char buf[200] = { 0 };
  11. strcpy(buf, arr);
  12. char*ret= strtok(arr, p);
  13. printf("%s\n", ret);
  14. ret= strtok(NULL, p);
  15. printf("%s\n", ret);
  16. ret= strtok(NULL, p);
  17. printf("%s\n", ret);
  18. return 0;
  19. }

       strtok函数找到str中的下一个标记,并将其用'\0'结尾,返回一个指向这个标记的指针(就是返回前面的地址)。

       上面这个函数写的很多余,很复杂。strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可修改。也就是说,平常我们会先拷贝源字符串,之后对拷贝字符串进行修改。 

       这里我们没有看其内部的细节,也没有模拟实现(真的懒了),但我们大致可以猜到它里面可能使用了static修饰变量使其生命周期延长。感兴趣的同学可以下去看看源码里面是如何定义的。

  1. int main()
  2. {
  3. char arr[] = "zpw@bitedu.tech";
  4. char* p = "@.";
  5. char tmp[20] = { 0 };
  6. strcpy(tmp, arr);
  7. //zpw\0bitedu\0tech\0
  8. char* ret = NULL;
  9. for (ret = strtok(tmp, p); ret != NULL; ret = strtok(NULL, p))
  10. {
  11. printf("%s\n", ret);
  12. }
  13. return 0;
  14. }

10.strerror函数

       这个函数会自动检查你是否使用错了别的函数,如果你使用函数错了,它会自动传一个数值进入这个函数(前提是你要调用它)。要引入errno.h的头文件。

       errno是一个全局错误码的变量,如果库函数执行错误,就会把对应错误码赋值到errno中,默认0就是没有错误。

  1. int main()
  2. {
  3. //C语言库函数
  4. // 错误码 错误信息
  5. //传0 - No error
  6. //传1 - Operation not permitted
  7. //传2 - No such file or directory
  8. //...
  9. //errno 是一个全局的错误码的变量
  10. //当C语言的库函数在执行过程中发生了错误,就会把对应的错误码赋值到errno中
  11. int i = 0;
  12. for (i = 0; i < 3; i++)
  13. {
  14. printf("%d: %s\n", i, strerror(i));
  15. }
  16. char* str = strerror(errno);//要引入errno.h的头文件
  17. return 0;
  18. }

        perror就是printf + strerror函数。

11.其他字符传函数 

islower判断字符是否为小写。

isdigit判断字符是否为数字。

tolower大写字符转小写字符,若还是小写字符,则就是小写字符。

toupper小写字符转大写字符若,还是大写字符,则就是大写字符。

  1. int main()
  2. {
  3. char ch = 'w';
  4. //int ret=islower(ch);//判断是不是小写字母
  5. int ret = isdigit(ch);//判断是不是数字
  6. char c= tolower('q');//大写转小写字母,如果本身就是小写字母就不动
  7. char b= toupper('Q');//小写转大写字母
  8. char arr[] = "I Am A Student";
  9. //大写字母转小写字母
  10. int i = 0;
  11. while (arr[i])
  12. {
  13. if (isupper(arr[i]))
  14. {
  15. arr[i]=tolower(arr[i]);
  16. }
  17. i++;
  18. }
  19. printf("%s\n", arr);
  20. return 0;
  21. }

       还有很多关于字符串的函数我们不可能一一去详细举例,我们也没有必要全部记住,我们需要的时候查找一下就可以。如果感觉写的还不错,请点点赞吧。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/71519?site
推荐阅读
相关标签
  

闽ICP备14008679号