【C进阶】第十四篇——字符串函数

简介: 【C进阶】第十四篇——字符串函数

strlen - 求字符串长度


函数介绍


size_t strlen( const char *string );

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

注意:

  • 参数指向的字符串要以'\0'结束
  • strlen返回的是在字符串中'\0'之前出现的字符个数(不包含'\0')
  • 注意函数的返回值为size_t,是无符号的(易错)

举个例子,比如我们要求字符串"abcded"的长度

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

运行结果:

image.png

模拟实现(三种方式)


方式一:计数器的方式

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

方式二:递归的方式

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

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

方式三:指针-指针的方式

进入函数体时,我们事先定义一个指针变量将传入的指针保存下来,然后将传入的指针向后移,直到遇到’\0‘时,我们返回当前指针与保存的指针的差值即可。

size_t my_strlen(const char* str)
{
  const char* p = str;
  while (*p != '\0')
  {
    p++;
  }
  return p-str;
}

strcpy - 字符串拷贝


函数介绍


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

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

注意:

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

代码示例:

#include<stdio.h>
#include<string.h>
int main()
{
  char arr1[10] = "abc";
  char arr2[] = "def";
  strcpy(arr1, arr2);
  printf("%s", arr1);
  return 0;
}

运行结果:"abc"被"def"覆盖了。

image.png

模拟实现


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

char* my_strcpy(char *dest, char *src)
{
  char* ret = dest;
  assert(dest != NULL && src != NULL);
  while (*dest++ = *src++)
  {
    ;
  }
  return ret;
}

strcat - 字符串追加


函数介绍


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

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

注意:

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

我们要将arr2数组中的"csdn!"追加到arr1数组的后面。

int main()
{
  char arr1[10] = "abc";
  char arr2[] = "def";
  strcat(arr1, arr2);
  printf("%s", arr1);
  return 0;
}

运行结果:

image.png

模拟实现


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

char* my_strcat(char* dest, char* src)
{
  assert(dest != NULL && src != NULL);
  char* ret = dest;
  while (*dest!='\0')
  {
    dest++;
  }
  while (*dest++ = *src++)
  {
    ;
  }
  return ret;
}

strcmp - 字符串比较


函数介绍


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

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

注意:

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

比如比较字符串"hello world!"和字符串"hello csdn!"的大小。

int main()
{
  char arr1[20] = "hello world!";
  char arr2[20] = "hello csdn!";
  int ret = strcmp(arr1, arr2);
  printf("%d", ret);
  return 0;
}

运行结果:比较字符串的时候发现前面字符的ASCll值都相同,知道比较到字符‘w'和字符'c'时,发现字符'w'的ASCll值大于字符'c'的ASCII值,于是返回一个大于0的数。

image.png

模拟实现


进入函数体直接比较起始位置的字符的大小。如果相同并且不为’\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;
}

strstr - 字符串查找


函数介绍


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

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

注意:

  • 若字符串2为空字符串,则返回字符串1的起始位置。

举个例子,比如我们在字符串"abcdefbcd"中查找字符串"bcd"。

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

运行结果:

image.png

注意:strstr函数的返回值是字符串"bcd"在字符串"abcdefbcd"中第一次出现的位置的起始位置,而不是出现几次就返回几个起始位置。

模拟实现


strstr函数的模拟实现相对复杂,在实现过程中我们需要设置3个指针变量来辅助实现函数功能。

cp指针: 记录每次开始匹配时的起始位置,当从该位置开始匹配时就找到了目标字符串,便于返回目标字符串出现的起始位置;当从该位置开始没有匹配成功时,则从cp++处开始下一次的匹配。

p1和p2指针: 通过判断p1和p2指针解引用后是否相等来判断每个字符是否匹配成功,若成功,则指针后移比较下一对字符;若失败,p1指针返回cp指针处,p2指针返回待查找字符串的起始位置。


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

刚刚开始时3个指针的指向如图所示:

image.png

若p1与p2匹配不成功,则cp指针后移,接着将cp指针赋值给p1指针:

image.png

此时,p1与p2匹配成功,那么cp指针不动,p1和p2指针后移继续比较:

image.png

当p1与p2匹配不成功时,cp指针后移一位,p1返回cp位置,p2返回待查找字符串起始位置:

image.png

从此位置开始下一轮的比较:

image.png

直到当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 *strToken, const char *strDelimit );

strtok函数能通过给定的一系列字符将一个字符串分割成许多子字符串的函数。它的第一个参数是需要被分割的字符串的首地址;第二个参数是一个字符串的首地址,该字符串是用作分隔符的字符集合。返回值是查找到的标记的首地址。

注意:

strtok函数找到strToken中的一个标记时,会将其用 \0结尾并返回这个标记的首地址

strtok函数会改变strToken函数,所以在使用strtok函数切分的字符串都是临时拷贝的内容并且可修改。

strtok函数的第一个参数不为NULL时,函数将找到strToken中的第一个标记,并保存它在字符串中的位置。

strtok函数的第一个参数为NULL时,函数将从同一个字符串中被保存的位置开始查找它的下一个标记。

若字符串中不存在更多的标记,则返回NULL指针。

举个例子,比如我们要将字符串"2957055542@qq.com"以"@“字符和”."字符分割开。

#include<stdio.h>
#include<string.h>
int main()
{
  char arr1[] = "2957055542@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;
}

image.png

注意:当strtok函数找到第一个标记时,将其后的’@‘字符改为’\0’并返回第一个标记的首地址,所以我们以返回的地址为首地址开始打印字符串的时候就只会打印出2957055542,第二次再对该字符串调用strtok函数时将从’@'字符后面开始寻找下一个标记。

strerror - 错误报告函数


函数介绍


char *strerror( int errnum );

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

perror - 错误报告函数


函数介绍


void perror( const char *string );

perror函数可以打印一个错误信息,无返回值。


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


举个例子:

注:fopen函数的功能是打开一个文件,当其执行成功时会返回打开文件的首地址,执行失败时会返回一个空指针(NULL)。

#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("fopen");
  }
  return 0;
}

运行结果:

image.png

当我们要打开一个不存在的文件(test.txt)来阅读的时候,显然fopen函数会执行失败,于是pf指针接收的便是空指针(NULL)。


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

perror: 直接打印错误信息,并且我们可以自己加上注释来明确错误来源于哪个库函数

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###"。

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值,所以返回一个负值。

strncat - 限制操作长度


函数介绍


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

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

注意:


当操作数小于源字符串中的字符个数时,操作数的大小决定被追加的字符个数,并在追加完后再追加一个’\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###";而当操作数为5时,拷贝结束后arr1数组中存放的是"abcdef\0##"。

相关文章
|
10月前
【C进阶】-- 字符串函数(1)(下)
【C进阶】-- 字符串函数(1)(下)
|
2月前
|
算法 C语言
【再识C进阶3(上)】详细地认识字符串函数、进行模拟字符串函数以及拓展内容
【再识C进阶3(上)】详细地认识字符串函数、进行模拟字符串函数以及拓展内容
|
3月前
|
算法 搜索推荐 程序员
C语言十八练——整数按位取反
C语言十八练——整数按位取反
47 0
|
3月前
|
存储 Java
第十四届蓝桥杯集训——字符串函数(基础函数操作)
第十四届蓝桥杯集训——字符串函数(基础函数操作)
41 0
|
9月前
|
算法 编译器 C语言
【C进阶】字符串函数
【C进阶】字符串函数
54 0
|
9月前
|
算法 搜索推荐 程序员
C语言第十六练——数字组合匹配
C语言第十六练——数字组合匹配
91 0
|
10月前
|
编译器 C语言
【C进阶】-- 字符串函数(1)(上)
【C进阶】-- 字符串函数(1)(上)
|
12月前
|
存储 人工智能 算法
2022 数据结构与算法《王道》学习笔记 (十)串 KMP算法 串的总结 课后习题笔记
2022 数据结构与算法《王道》学习笔记 (十)串 KMP算法 串的总结 课后习题笔记
|
存储 安全 C语言
【C语言】常用字符串函数大全(超详笔记收藏必备)(下)
【C语言】常用字符串函数大全(超详笔记收藏必备)(下)
47 0
|
存储 C语言
【C语言】常用字符串函数大全(超详笔记收藏必备)(上)
【C语言】常用字符串函数大全(超详笔记收藏必备)(上)
160 0