本文收录于专栏:C语言进阶

关注作者,持续阅读作者的文章,学习更多知识!
https://blog.csdn.net/weixin_53306029?spm=1001.2014.3001.5343


C语言中对字符和字符串的处理很是频繁,但是C语言本身是没有字符串类型的,字符串通常放在常量字符串 中或者 字符数组 中。

字符串常量 适用于那些对它不做修改的字符串函数.

但比较友好的是C语言有处理字符和字符串的一些库函数,方便我们对字符和字符串进行一些操作。

所以本节我会重点介绍处理字符和字符串的一些常用库函数,让大家学会使用,并且了解它们的实现原理和一些注意事项。

str家族

    • strlen - 求字符串长度
      • 1.函数介绍
      • 2.strlen模拟实现(三种方式)
        • 1.计数器
        • 2.递归
        • 3.指针
    • strcpy - 字符串拷贝
      • 1.函数介绍
      • 2.strcpy模拟实现
    • strcat - 字符串追加
      • 1.函数介绍
      • 2.strcat模拟实现
    • strcmp - 字符串比较
      • 1.函数介绍
      • 2. strcmp模拟实现
    • strncpy、strncat、strncmp - 限制操作长度
      • 1.strncpy
      • 2.strncat
      • 3.strncmp
    • strstr - 字符串查找
      • 1.函数介绍
      • 2.strstr模拟实现
    • strtok - 字符串分割
    • strerror、perror - 错误报告函数
      • 1.strerror
      • 2.perror
  • – the End –

strlen - 求字符串长度

1.函数介绍

 size_t strlen( const char *string );

strlen函数是一个用于求字符串长度的库函数。它的参数是被求长度的字符串的起始地址,返回值是一个无符号整型。
举例:

#include<stdio.h>
#include<string.h>
int main()
{char arr[] = "helloworld";size_t ret = strlen(arr);return 0;
}

我们定义一个size_t型的变量ret接收函数的返回值,即字符串长度,结果是10

注意

  • 1.参数指向的字符串要以’\0’结束。
  • 2.strlen返回的是在字符串中’\0’之前出现的字符个数(不包含’\0’)。

int main()
{//char arr[] = "abc";//'c'后面是'\0',所以字符串长度是3char arr[] = { 'a','b','c' };//'c'后面不一定是'\0',所以算出的字符串长度是随机值size_t len = strlen(arr);printf("%d\n", len);return 0;
}

运行结果:


  • 3.注意函数的返回值为size_t,是无符号的(易错)。

:下面运行结果应该是什么?

if (strlen("abc") - strlen("abcdef") > 0){printf(">\n");}else{printf("<=\n");}return 0;

答案是大于
strlen接收的是无符号整形,两个无符号整数相减在算数层面还是无符号整数,原本的-3不算符号位,即补码当作原码,会变成一个很大的整数。
很显然,如果我们事先不知道strlen函数的返回值为size_t,很容易认为结果是<=

2.strlen模拟实现(三种方式)

1.计数器

我们定义一个变量为count,如果传入的指针指向的内容不是’\0’,那么count++,同时指针后移一位,循环往复,直到找到’\0’时返回count即可。

size_t my_strlen1(const char* str)
{size_t count = 0;//计数器while (*str){count++;str++;}return count;
}

2.递归

我们一进入函数体就判断传入指针指向的内容是否为’\0’,如果是就返回0,不是就返回1+my_strlen2(str+1),如此进行下去,直到递归到字符串结尾找到’\0’,这时再一步步将值返回回来即可。

size_t my_strlen2(const char* str)
{if (*str == '\0')return 0;elsereturn 1 + my_strlen2(str + 1);
}

3.指针

进入函数体时,我们事先定义一个指针变量将传入的指针保存下来,然后将传入的指针向后移,直到遇到’\0’时,我们返回当前指针与保存的起始位置指针的差值,即是字符串长度。(指针与指针的差的绝对值是两个指针之间的元素个数)

size_t my_strlen3(const char* str)
{const char* p = str;//保存起始位置while (*str != '\0')str++;return str - p;
}

strcpy - 字符串拷贝

1.函数介绍

char *strcpy( char *Destination, const char *Source );

strcpy函数是一个用于拷贝字符串的函数,即将一个字符串中的内容拷贝到另一个字符串中(会覆盖原字符串内容)。它的参数是两个指针,第一个指向的是拷贝字符串的目的地的起始位置,即要将字符串拷贝到什么地方;第二个指向的是要拷贝字符串的内容的起始位置,即需要拷贝的字符串。它的返回值是目标空间的起始位置。

注意:

  • 源字符串(需要被拷贝的字符串)必须以’\0’结束。
  • 会将源字符串中的’\0’一同拷贝到目标空间。
  • 目标空间必须足够大,以确保能存放源字符串。
  • 目标空间必须可变。

举例:

int main()
{char arr[20] = { 0 };//arr = "hello";//err char arr1[10] = "abcaaa";char arr2[] = "def";strcpy(arr1, arr2);printf("%s\n", arr1);return 0;
}

运行结果是def


深入来看,我们通过监视看到:
完成拷贝后,arr1只被覆盖了"abca"前四个字符,即拷贝了arr2中的"def"和’\0’,但是输出时默认遇到’\0’结束,所以打印结果只显示"def"三个字符

2.strcpy模拟实现

进入函数体时先定义一个指针变量保存目标空间的起始位置,便于之后返回。然后将源字符串中的字符一一赋值给目标空间,直到遇到源字符串中的’\0’,将’\0’也赋值给目标空间后结束赋值,最后返回目标空间的起始位置。

char* my_strcpy(char* dest, char* src)
{char* ret = dest;//保存目标空间的起始位置assert(dest != NULL);//断言,dest为空指针时报错assert(src != NULL);//断言,src为空指针时报错while (*dest++ = *src++){;}return ret;
}

strcat - 字符串追加

1.函数介绍

char *strcat( char *Destination, const char *Source );

strcat函数是一个用于追加字符串的函数,即将一个字符串中的内容追加到另一个字符串后面(不会覆盖原字符串内容)。它的参数是两个指针,第一个指向的是追加字符串的目的地的起始位置,即要将字符串追加到哪个字符串后面;第二个指向的是要追加字符串的内容的起始位置,即需要追加的字符串。它的返回值是目标空间的起始位置。

注意:

  • 源字符串必须以’\0’结束。
  • 目标空间必须足够大,能容纳下源字符串的内容。
  • 目标空间必须可修改。
  • 字符串不能给自己追加(’\0’被覆盖,无终止条件)。

举例:

#include<stdio.h>
#include<string.h>
int main()
{char arr1[20] = "hello ";char arr2[] = "world!";strcat(arr1, arr2);return 0;
}

追加结束后arr1数组中的内容变成"hello world!"。

2.strcat模拟实现

进入函数体依然先定义一个指针变量用于存放目标空间的起始位置,便于之后返回。然后用循环先找到目标空间的’\0’,之后从’\0’的位置开始追加源字符串的内容,直到追加到源字符串中的’\0’为止。最后返回目标空间的起始位置。

char* my_strcat(char* dest, const char* src)
{assert(dest != NULL);//断言,dest为空指针时报错assert(src != NULL);//断言,src为空指针时报错char* ret = dest;//找到目标空间的'\0'while (*dest){dest++;}//追加while (*dest++ = *src++){;}return ret;
}

strcmp - 字符串比较

1.函数介绍

int strcmp( const char *string1, const char *string2 );

strcmp函数是一个用于比较两个字符串内容的函数。它的参数是两个指针,指针分别指向两个待比较字符串的起始位置。它的返回值是一个整型数字。当string1大于string2的时候返回一个大于0的数;当string1等于string2的时候返回0;当string1小于string2的时候返回一个小于0的数。

注意:

  • 字符串比较的不是字符串长度的大小,而是两个字符串中对应位置字符的ASCII值。

举例:

#include<stdio.h>
#include<string.h>
int main()
{char arr1[20] = "hello world!";char arr2[20] = "hello bitt!";int ret = strcmp(arr1, arr2);return 0;
}

比较字符串的时候发现前面字符的ASCII值都相同,直到比较到字符’w’和字符’b’时,发现字符’w’的ASCII值大于字符’b’的ASCII值,于是返回一个大于0的数。

2. strcmp模拟实现

进入函数体直接比较起始位置的字符的大小。如果相同并且不为’\0’那么继续比较下一对字符的大小;如果相同并且为’\0’那么说明字符串比较完毕,那么直接返回0;如果不同则直接返回str1与str2中对应字符的ASCII值的差值(当str1中对应字符大于str2中的对应字符时返回正值,当str1中对应字符小于str2中的对应字符时返回负值)。

int my_strcmp(const char* str1, const char* str2)
{assert(str1 != NULL);//断言,str1为空指针时报错assert(str2 != NULL);//断言,str2为空指针时报错while (*str1 == *str2){if (*str1 == '\0')//字符串全部比较完毕return 0;str1++;str2++;}return *str1 - *str2;
}

strncpy、strncat、strncmp - 限制操作长度

我们发现strcpy是将一个字符串全部拷贝到另一个字符串,strcat是将一个字符串全部追加到另一个字符串后面,strcmp也是比较两个字符串的全部内容,这类操作函数称为长度不受限制的字符串操作函数。

那么我们如果操作字符串时并不想操作整个字符串,而只想操作字符串的一部分怎么办呢?

库函数中的strncpy、strncat、strncmp便解决了这个问题。

1.strncpy

char *strncpy( char *Dest, const char *Source, size_t count );

strncpy的参数与strcpy相比较多出了一个参数,而这个参数就是需要被操作的字符个数。

注意:

  • 当操作数小于等于源字符串中的字符个数时,操作数的大小决定被拷贝的字符个数。
  • 操作数大于源字符串中字符的个数时,strncpy函数将源字符串中的字符拷贝到目标空间后不够的将用’\0’填充。

举例:

#include<stdio.h>
#include<string.h>
int main()
{char arr1[10] = "##########";char arr2[] = "abcd";strncpy(arr1, arr2, 3);strncpy(arr1, arr2, 6);return 0;
}


当操作数为3时,拷贝结束后arr1数组中存放的是"abc#######"

当操作数为6时,拷贝结束后arr1数组中存放的是"abcd\0\0####"

2.strncat

char *strncat( char *Dest, const char *Source, size_t count );

strncat的参数与strcat相比较也多出了一个参数,而这个参数也就是需要被操作的字符个数。

注意:

  • 当操作数小于源字符串中的字符个数时,操作数的大小决定被追加的字符个数,并在追加完后再追加一个’\0’。
  • 当操作数大于等于源字符串中的字符个数时,将源字符串内容全部追加到目标空间便结束追加,并在追加完后再追加一个’\0’表示结束。

举例:

#include<stdio.h>
#include<string.h>
int main()
{char arr1[10] = "abc\0#####";char arr2[] = "def";strncat(arr1, arr2, 2);strncat(arr1, arr2, 5);return 0;
}


当操作数为2时,拷贝结束后arr1数组中存放的是"abcde\0###"

注意:从监视我们可以看出,这里所谓的追加其实是在要追加的目标字符串遇到’\0’的位置开始进行覆盖,除去覆盖位置arr1原本存在的字符并不会显示,只是因为输出时一般默认遇到’\0’结束,所以才达到所谓字符串追加的效果。


当操作数为5时,拷贝结束后arr1数组中存放的是"abcdef\0##"

3.strncmp

int strncmp( const char *string1, const char *string2, size_t count );

strncmp的参数与strcmp相比较也多出了一个参数,而这个参数也就是需要比较的字符个数。

举例:

#include<stdio.h>
#include<string.h>
int main()
{char arr1[] = "abcde";char arr2[] = "abcdf";int ret1 = strncmp(arr1, arr2, 4);int ret2 = strncmp(arr1, arr2, 5);return 0;
}
  • 当操作数为4时,我们只比较了arr1和arr2的前4个字符,而它们前4个字符都相同,所以返回的是0;
  • 而当操作数为5的时候,我们比较了arr1和arr2的前5个字符,因为字符’e’的ASCII值小于字符’f’的ASCII值,所以返回一个负值。

strstr - 字符串查找

1.函数介绍

char *strstr( const char *string, const char *strCharSet );

strstr函数可以在一个字符串(字符串1)中查找另一个字符串(字符串2),如果字符串2存在于该字符串1中,那么就返回字符串2在字符串1中第一次出现的起始位置,如果在字符串1中找不到字符串2,那么就返回空指针(NULL)。它的第一个参数是字符串1的起始位置,第二个参数是字符串2的起始位置。

注意:

  • 若字符串2为空字符串,则返回字符串1的起始位置。
  • strstr函数返回的是第一次出现的位置的起始位置,而不是出现几次就返回几个起始位置。

举例:在字符串"abcdefbcdef"中查找字符串"cde"

#include<stdio.h>
#include<string.h>
int main()
{char arr1[] = "abcdefbcdef";char arr2[] = "cde";char* ret = strstr(arr1, arr2);//在arr1中查找arr2字符串第一次出现的位置if (ret != NULL)printf("%s\n", ret);elseprintf("找不到\n");return 0;
}

2.strstr模拟实现

我们需要设置3个指针变量来辅助实现函数功能。

  • cp指针: 记录每次开始匹配时的起始位置,当从该位置开始匹配时就找到了目标字符串,便于返回目标字符串出现的起始位置;当从该位置开始没有匹配成功时,则从cp++处开始下一次的匹配。
  • p1和p2指针: 通过判断p1和p2指针解引用后是否相等来判断每个字符是否匹配成功,若成功,则指针后移比较下一对字符;若失败,p1指针返回cp指针处,p2指针返回待查找字符串的起始位置。

实例:在字符串"abbbcdef"中查找字符串"bbc":


直到当p2指向的内容为\0时,便说明待查找字符串中的字符已经被找完,也说明了从当前cp位置开始匹配能够找到目标字符串,所以此时返回指针cp即可。

代码实现:

char* my_strstr(const char* str1, const char* str2)
{assert(str1 != NULL);//断言,当str1为空指针报错assert(str2 != NULL);//断言,当str2为空指针报错const char* cp = str1;//记录开始匹配时的起始位置if (*str2 == '\0')//要查找的字符串为空字符串return (char*)str1;while (*cp){const char* p1 = cp;const char* p2 = str2;while ((*p1!='\0') && (*p2!='\0') && (*p1 == *p2)){p1++;p2++;}if (*p2 == '\0')//目标字符串已被查找完return (char*)cp;cp++;}return NULL;//找不到目标字符串
}

strtok - 字符串分割

函数原型:

char * strtok (char *str, const char * delimiters);

参数:

str,待分割的字符串;delimiters,分割符字符串。

该函数用来将字符串分割成一个个片段。参数str指向欲分割的字符串,参数delimiters则为分割字符串中包含的所有字符。当strtok()在参数str的字符串中发现参数delimiters中包涵的分割字符时,则会将该字符改为\0 字符。在第一次调用时,strtok()必需给予参数str字符串,往后的调用则将参数s设置成NULL。每次调用成功则返回指向被分割出片段的指针。

需要注意的是,使用该函数进行字符串分割时,会破坏被分解字符串的完整,调用前和调用后的s已经不一样了。所以我们可以将待分割的字符串拷贝一份使用,防止原数据被修改。

注意:

  • strtok函数找到str中的一个标记时,会将其用 \0结尾(即用’\0’覆盖)并返回这个标记的首地址。
  • strtok函数会改变str函数,所以在使用strtok函数切分字符串时应该临时拷贝一份,并且内容可修改。
  • strtok函数的第一个参数不为NULL时,函数将找到str中的第一个标记,并保存它在字符串中的位置。
  • strtok函数的第一个参数为NULL时,函数将从同一个字符串中被保存的位置开始查找它的下一个标记。
  • 若字符串中不存在更多的标记,则返回NULL指针。

举例:

#include<stdio.h>
#include<string.h>
int main()
{char arr1[] = "275207042@qq.com";//待分割字符串char arr2[] = "@.";//分隔符的字符集合char arr3[20] = { 0 };strcpy(arr3, arr1);//将数据拷贝一份使用,防止原数据被修改char* token = strtok(arr3, arr2);//第一次传参需传入待分割字符串首地址while (token != NULL)//说明还未分割完{printf("%s\n", token);token = strtok(NULL, arr2);//第二次及以后的第一个参数设为NULL}return 0;
}

运行结果:

通过 ‘@’ 和 ‘.’ 将 “275207042@qq.com” 分为三部分。

strerror、perror - 错误报告函数

1.strerror

char *strerror( int errnum );

strerror函数可以把错误码转换为对应的错误信息,返回错误信息对应字符串的起始地址。

#include <errno.h>//必须包含的头文件
我们需要知道,库函数在使用的时候如果发生错误,都会有对应的错误码,而这些错误码都会被存放在errno这个全局变量中,如果要使用这个全局变量,我们需要引其对应的头文件:#include<errno.h>

举例:

strerror: 只负责将错误码转换为对应的错误信息,不打印

和strerror有相似功能的还有perror函数

2.perror

void perror( const char *string );

相比于strerror函数,perror函数会首先把错误码转化为错误信息,然后打印错误信息(包含了自定义的信息),更方便实用。

举例:

#include<stdio.h>
#include<string.h>
#include<errno.h>
int main()
{//打开文件失败的时候,会返回空FILE* pf = fopen("test.txt", "r");//打开test.txt文件阅读if (pf == NULL){//两者比较printf("%s\n", strerror(errno));perror();return 0;
}


很显然,同样的效果,perror使用起来更简洁方便。


– the End –

好了,今天关于“str家族”的分享就到这里了。
期待下次再见~~~

【str家族】如何使用处理字符和字符串的库函数相关推荐

  1. C语言字符与字符串的库函数

    本章重点介绍字符和字符串的库函数使用,C语言本身是没有字符串类型的,字符串通常存放在常量字符串或字符数组中.字符串常量适用于那些对它不做修改的字符串函数. 1.strlen:求字符串的长度(不包括  ...

  2. 给定一个字符串str,将str中连续两个字符为a的字符替换为b(一个或连续超过多个字符a则不替换)...

    需求:给定一个字符串str,将str中连续两个字符为a的字符替换为b(一个或连续超过多个字符a则不替换) 如:  a 不替换  b 不替换  ab 不替换  ba 不替换  aba 不替换  aab ...

  3. Python中两个list取交集、并集、差集以及为字符串str添加、插入特定字符的操作总结

    Python中两个list取交集.并集.差集以及为字符串str添加.插入特定字符的操作总结 Python中两个list取交集.并集.差集 为字符串str添加.插入特定字符的操作总结 Python中两个 ...

  4. SQL分割字符串,SQL按照指定字符分割字符串,SQL处理字符串...

    SQL分割字符串,SQL按照指定字符分割字符串,SQL处理字符串 -----原文来源于网络  T-SQL对字符串的处理能力比较弱,比如我要循环遍历象1,2,3,4,5这样的字符串,如果用数组的话,遍历 ...

  5. java 字符转化字符串_【转载】java字符串的各种编码转换

    来自:http://www.blogjava.net/rabbit/archive/2008/03/27/189009.html import java.io.UnsupportedEncodingE ...

  6. 重温CLR(十) 字符、字符串和文本处理

    本章将介绍.net中处理字符和字符串的机制 字符 在.NET Framewole中,字符总是表示成16位Unicode代码值,这简化了国际化应用程序的开发. 每个字符都表示成System.Char结构 ...

  7. java中的字符,字符串,数字之间的转换(亲测)

    string 和int之间的转换 string转换成int  :Integer.valueOf("12") int转换成string : String.valueOf(12) ch ...

  8. android 查找字符在字符串的位置

    昨天,自己用到在字符串内查找一个字符串的位置,主要用到了  indexOf()的代码,这个是判断字符在字符串的第一次出现的位置.今天,自己没有什么好写的,所以决定把这个记录一下.也是很有用的. J ...

  9. python字符复制函数是啥_Python最全的字符和字符串函数,直接复制到IDLE或另存为py可以运行...

    ## -*- coding: utf-8 -*- import string str1 = "0123456789" print str1[0:3] ##截取第一位到第三位的字符 ...

最新文章

  1. 在Ubuntu 14.04 64bit上编译安装Crtmpserver trunk svn 811版本!
  2. 安全起见,这款 IDEA 插件赶紧删了吧!
  3. [翻译] VLDContextSheet
  4. 32接上拉5v_51单片机P0口上拉电阻的选择
  5. 【轻量级网络】MobileNet-v1详解
  6. python列表功能默写_python基础学习——列表list的功能
  7. 【李宏毅2020 ML/DL】补充:Meta Learning - Gradient Descent as LSTM
  8. JAVA中如何全局监听鼠标事件
  9. 中文停用词表(1893个)
  10. 使用Flutter开发一个仿微信飞机大战游戏
  11. 项目文档:IT项目管理
  12. 基于opencv 的OCR小票识别(1)
  13. python 正态分布函数_正态分布概率计算
  14. Lum Proxy全球代理IP,真实家庭住宅网络!
  15. 【电路设计】Altium Designer 20 PCB设计
  16. 餐饮日销售情况分析仪
  17. python 识别图像中的文字(数字)之python图文识别
  18. 看了这篇干货,再也不怕Mac内存不足了!
  19. OpenCV小案例(2)——判断一张图片中多少种颜色
  20. js函数使用详细讲解!

热门文章

  1. 网络编程——线程竞争
  2. 提高钢材品质应用 高精度在线测径仪
  3. 马云说聪明的人都离开了阿里,剩下的成了富翁
  4. 说说微信红包算法,为什么你总是抢的那么少?
  5. OmniPlan 3破解码
  6. d作者:d中导入C编程
  7. 服务器和工作站有什么区别?
  8. 2016年苹果开发者账号申请
  9. 百度SEO教程-利于百度推送工具实现百度快速收录
  10. Docker 无法启动 Failed to start LSB: Create lightweight, portable, self-sufficient containers.