【C语言】字符串函数介绍三(strstr、strtok、streeror)

简介: 【C语言】字符串函数介绍三(strstr、strtok、streeror)

前言

之前我们用两篇文章介绍了strlen、strcpy、stract、strcmp、strncpy、strncat、strncmp这些函数

第一篇文章strlen、strcpy、stract

第二篇文章strcmp、strncpy、strncat、strncmp

今天我们就来学习:

话不多说,我们直接开始

strstr

返回值

如果s2是s1的子串,就返回子串的首元素地址,

如果没找到,就返回空指针

补充说明

当s1中存在多个s2时,其返回的地址,是s2在s1中第一次出现的地址

一般情况

int main()
{
  char* p1 = "abcdefabcdef";
  char* p2 = "def";
  char * ret = strstr(p1, p2);
  if (ret == NULL)
  {
    printf("0\n");
  }
  else
  {
    printf("%s\n", ret);
  }
  return 0;
}

模拟实现

基本思路

两种情况:

*s1 == *s2

*s1 != *s2

*s1 == *s2

s1向后移动,s2也向后移动,看是否继续相等

while (*s1 == *s2)
{
  s1++;
  s2++;
}
*s1 != *s2

s1不动,s2向后移动一位,看二者是否相等

while (*s1 == *s2)
{
  s1++;
  s2++;
}
s1++;

问题1

当s2是’\0‘时,说明要查找的字符串已经结束了,这时就结束查找

问题2

当s1是’\0‘时,说明被查找的字符串已经结束了,这时就结束查找

问题3

当我们真的找到这个子串的时候,想要返回首元素地址,却发现s2指向的是字串的最后一个元素。

问题4

当字符串1为“abbbc”,字符串2为“bbc”时,

是得不到正确结果的,因为s2指向的已经是第二个b了,s1指向的也是第二个b

解决方案

想解决问题三和四,就单独创建两个变量p1、p2来存储s1和s2就行了

并且,当s1和s2指向的元素相等时,要创建一个变量cur来记录这个位置,方便s1和s2回到这个位置继续查找。

最终代码

char* my_strstr(const char* s1, const char* s2)
{
  assert(s1 && s2);
  if (*s2 == 0)//当s2是空字符串时,传进来的s2是‘\0’
  {
    return (char*)s1;//此时直接返回s1即可
  }
  char* p1 = s1;
  char* p2 = s2;
  char* cur = s1;
  while (*cur)
  {
    p1 = cur;
    p2 = s2;
    while ((*p1 == *p2) && (*p1 != '\0') && (*p2 != '\0'))
    {
      p1++;
      p2++;
    }
    if (*p2 == '\0')
    {
      return cur;
    }
    cur++;
  }
  return NULL;
}
int main()
{
  char* p1 = "abcdef";
  char* p2 = "def";
  char* ret = strstr(p1, p2);
  if (ret == NULL)
  {
    printf("0\n");
  }
  else
  {
    printf("%s\n", ret);
  }
  return 0;
}

写的还是比较清晰的,有疑问可以在文章下面留言~

strstr函数在编译器中的实现

我没在库里找到这个文件,所以在网上复制了一份源码

#include <stdio.h>
char * __cdecl strstr(const char *str1, const char *str2)
{
    char *cp = (char *)str1;
    char *s1, *s2;
    if (!*str2)
        return((char *)str1);
    while (*cp)
    {
        s1 = cp;
        s2 = (char *)str2;
        while (*s2 && !(*s1 - *s2))
            s1++, s2++;
        if (!*s2)
            return(cp);
        cp++;
    }
    return(NULL);
}
int main(int argc, char *argv[], char *envp[])
{
    char str[] = "asfasfas";
    char *p = "asas";
    char ret = '0';
    ret = strstr(str, p);
    if (ret != NULL)
    {
        printf("%c", ret);
    }
    else
    {
        printf("NULL");
    }
    return 0;
}

补充说明,在源代码中,我们可以看到参数都用const保护起来了,所以在返回值那里需要进行强制类型转换(char*)

//虽然不转换代码也能运行,但还是严谨一点好

拓展

KMP算法和上面的实现过程结果是一样的,但更高级,感兴趣的可以去了解一下

strtok(会用即可)

介绍

  1. sep参数是个字符串,定义了用作分隔符的字符合集
    2.第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标
    记。
    3.strtok函数找到str中的下一个标记,并将其用 \0结尾,返回一个指向这个标记的指针。(注: strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可修改。)
    4.strtok函数的第一个参数不为 NULL ,函数将找到str中第一个标记,strtok函数将保存它在字符串 中的位置。
    5.strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标 记。
    6.如果字符串中不存在更多的标记,则返回NULL 指针。

详细解释

下面我们看一段代码,来实际应用一下

int main()
{
  char arr[] = "hello@world.nicetomeetyou";
  char* p = "@.";
  char buff[10000] = { 0 };
  strcpy(buff, arr);
  char* ret = strtok(arr, p);
  printf("%s\n", ret);
  return 0;
}

1.p就是sep,里面的字符就是分隔符

2.arr就是str

3.strtok函数会从str开始,逐个向后查找,当找到sep中的分隔符时,strtok就将它改成’\0’,并返回分割出的这个字符串的首元素地址。

下一次查找就从\0后面开始寻找(等于是创建了一个静态变量存储’\0’后面的元素的地址)

4.当第二个参数不为NULL时,从sep中的的第一个分隔符开始查找

5.当第二个参数为NULL时,那就进入sep中的下一个分隔符开始查找

意思就是:查找第二个分隔符

6.当字符串str中遇到\0时,就结束查找,并返回NULL

注意:

strtok函数会破坏被查找的字符串,所以我们需要将原字符串拷贝一份,再进行查找

实际应用

像上文那样使用的话,有几个字符串,就得写几次

但实际上,我们并不会像上面那段代码那样使用strtok函数,

在上文“详细解释”中,第六点提到:当str中遇到\0时,就返回NULL

所以我们可以先创建一个变量ret定义为NULL

使用for循环来打印出分割的多个字符串

for (ret = strtok(arr, p); ret != NULL; ret = strtok(NULL, p))
  {
    printf("%s\n", ret);
  }

(很神奇的一种使用方式)

strerror

介绍

char * strerror ( int errnum )

返回错误码,所对应的错误信息

使用方法

传入一个整数,输出一个地址

int main()
{
  char* str = strerror(0);
  printf("%s\n", str);
  return 0;
}

运行结果:

此处传入的整数,被称为错误码

每个错误码,都对应一个错误信息

如:

0:No error

1:Operation not permitted

实际使用

但在实际应用中,错误码不会由我们传入,而是输入errno,

errno是一个全局的错误码变量

当C语言的库函数在执行过程中,发生了错误,就会把对应的错误码,赋值给errno

需要包含头文件<errno.h>

举例

C语言中我们使用fopen函数来打开一个文件

int main()
{
  FILE* pf = fopen("test.txt", "r");
  //要打开文件的名称是test.txt,打开方式是"r",读取这个文件
  //这个函数会返回一个FILE*的指针
  if (pf == NULL)
  //当返回为空指针,说明读取失败
  //但读取失败有很多原因,可能是文件不存在,可能是访问权限不够等等
  {
    printf("%s\n", strerror(errno));
  }
  else
  {
    printf("open file success\n");
  }
  return 0;
}

这里我们就可以通过strerror函数,来找到确切的原因

运行结果

没有这样的文件或目录

errno

想知道更多可以直接转到定义查看不同的整数对应的错误信息

结语

字符串函数就介绍到这里了,下一篇文章我们会学习字符分类函数

我们明天见~



相关文章
|
2月前
|
C语言 C++
【C语言】解决不同场景字符串问题:巧妙运用字符串函数
【C语言】解决不同场景字符串问题:巧妙运用字符串函数
|
2月前
|
存储 C语言
【c语言】字符串函数和内存函数
本文介绍了C语言中常用的字符串函数和内存函数,包括`strlen`、`strcpy`、`strcat`、`strcmp`、`strstr`、`strncpy`、`strncat`、`strncmp`、`strtok`、`memcpy`、`memmove`和`memset`等函数的使用方法及模拟实现。文章详细讲解了每个函数的功能、参数、返回值,并提供了具体的代码示例,帮助读者更好地理解和掌握这些函数的应用。
27 0
|
2月前
|
存储 安全 编译器
深入C语言库:字符与字符串函数模拟实现
深入C语言库:字符与字符串函数模拟实现
|
2月前
|
C语言
C语言常见字符函数和字符串函数精讲
C语言常见字符函数和字符串函数精讲
|
2月前
|
C语言
【C语言】模拟实现深入了解:字符串函数
【C语言】模拟实现深入了解:字符串函数
|
4月前
|
C语言
【C语言篇】字符和字符串以及内存函数详细介绍与模拟实现(下篇)
perror函数打印完参数部分的字符串后,再打印⼀个冒号和⼀个空格,再打印错误信息。
64 0
|
4月前
|
存储 安全 编译器
【C语言篇】字符和字符串以及内存函数的详细介绍与模拟实现(上篇)
当然可以用scanf和printf输入输出,这里在之前【C语言篇】scanf和printf万字超详细介绍(基本加拓展用法)已经讲过了,这里就不再赘述,主要介绍只针对字符的函数.
56 0
|
2月前
|
C语言 C++
C语言 之 内存函数
C语言 之 内存函数
36 3
|
18天前
|
C语言
c语言调用的函数的声明
被调用的函数的声明: 一个函数调用另一个函数需具备的条件: 首先被调用的函数必须是已经存在的函数,即头文件中存在或已经定义过; 如果使用库函数,一般应该在本文件开头用#include命令将调用有关库函数时在所需要用到的信息“包含”到本文件中。.h文件是头文件所用的后缀。 如果使用用户自己定义的函数,而且该函数与使用它的函数在同一个文件中,一般还应该在主调函数中对被调用的函数做声明。 如果被调用的函数定义出现在主调函数之前可以不必声明。 如果已在所有函数定义之前,在函数的外部已做了函数声明,则在各个主调函数中不必多所调用的函数在做声明
31 6