strstr,strnstr函数详解

 strstr函数

strstr函数是C语言中的一个字符串函数,用于在一个字符串中查找另一个字符串的出现位置。

它的函数原型如下:

char *strstr(const char *haystack, const char *needle);

在这个函数中,haystack表示被搜索的字符串,needle表示要查找的子字符串。函数的返回值是一个指针,指向第一次出现needle的位置,如果没有找到,返回NULL。

例如,我们可以使用strstr函数来查找一个字符串中是否包含另一个字符串:

#include <stdio.h>
#include <string.h>int main() {char haystack[] = "Hello, world!";char needle[] = "world";char *result = strstr(haystack, needle);if (result != NULL) {printf("'%s' is found in '%s' at position %ld.\n", needle, haystack, result - haystack);} else {printf("'%s' is not found in '%s'.\n", needle, haystack);}return 0;
}

上述代码输出为:

'world' is found in 'Hello, world!' at position 7.

需要注意的是,strstr函数是区分大小写的,如果要进行大小写不敏感的查找,可以使用其他函数,例如strcasestr。

使用示例

查找子字符串成功后,用strncpy修改内存块内容。

#include <stdio.h>
#include <string.h> 
#include<assert.h>int main()
{char str[] = "This is a simple string hello world!";char* pch;pch = strstr(str, "string");if (pch != NULL){printf("%s\n",str);printf("%s\n",pch);strncpy(pch, "sifang",6);}printf("%s\n",str);printf("%s\n",pch);return 0;
}


输出结果:

 由输出的结果可知,pch的内容为str的子字符串,指向的地址是第一次相同的位置,也就是说pch指向的内存块是str所指向的内存块。当用strncpy修改pch所指向的内存块值时,其str所指向的对应内存块内容发生改变。

用memcpy替换strncpy,其输出的结果是相同的。

查找子字符成功,strncp的长度修改,引起结果异常。

#include <stdio.h>
#include <string.h> 
#include<assert.h>int main()
{char str[] = "This is a simple string hello world!";char* pch;pch = strstr(str, "string");if (pch != NULL){printf("%s\n",str);printf("%s\n",pch);strncpy(pch, "sifang",7);//只是将6修改为7}printf("%s\n",str);printf("%s\n",pch);return 0;
}

输出结果:

对比上面输出的结果可以看出,strncpy后,后面对字符串做了截断处理,这是因为strncpy时,只有sifang六个字符,而长度为7 ,因此将\0的结束字符也拷贝了,引起截断。这种情况和用strcpy的结果是一样的,因为strcpy拷贝自动带上结束符\0。

#include <stdio.h>
#include <string.h> 
#include<assert.h>int main()
{char str[] = "This is a simple string hello world!";char* pch;pch = strstr(str, "string");if (pch != NULL){printf("%s\n", str);printf("%s\n", pch);strcpy(pch, "sifang");}printf("%s\n", str);printf("%s\n", pch);return 0;
}

结果 

模拟实现

#include <stdio.h>  
#include <string.h>  
#include <assert.h>  char *my_strstr(const char *haystack, const char *needle) {  assert(haystack != NULL && needle != NULL); // 确保两个指针都不是NULL  if (*needle == '\0') {  // 如果needle是空字符串,则认为在haystack的开头就找到了  return (char *)haystack;  }  while (*haystack != '\0') {  const char *h = haystack;  const char *n = needle;  // 逐个字符比较haystack和needle  while (*h != '\0' && *n != '\0' && *h == *n) {  h++;  n++;  }  // 如果needle已经完全匹配,返回haystack的起始位置  if (*n == '\0') {  return (char *)haystack;  }  // 否则移动haystack到下一个字符  haystack++;  }  // 没有找到匹配项  return NULL;  
}  int main() {  const char *haystack = "Hello, world!";  const char *needle = "world";  char *result = my_strstr(haystack, needle);  if (result != NULL) {  printf("Found '%s' in '%s' at position: %ld\n", needle, haystack, result - haystack);  } else {  printf("'%s' not found in '%s'.\n", needle, haystack);  }  return 0;  
}

strnstr函数

strnstr函数是C语言标准库中的一个函数,用于在指定长度的字符串中查找第一次出现子串的位置。它的原型通常如下:

 char *strnstr(const char *s1, const char *s2, size_t n); 

其中:

  1. s1 是主字符串,即要搜索的字符串。
  2. s2 是子字符串,即要在 s1 中查找的字符串。
  3. n 是指定 s1 中参与搜索的最大字符数。

strnstr 函数从 s1 的开头开始,搜索 s2 在 s1 中首次出现的位置,但只搜索到 s1 的前 n 个字符。如果找到 s2,则返回指向 s1 中 s2 首次出现的位置的指针;如果没有找到,则返回 NULL。

这个函数在处理可能包含子串的字符串时非常有用,尤其是当你不确定整个字符串是否都包含在内,或者只关心字符串的一部分时。通过限制搜索的长度,你可以避免不必要的内存访问,并提高性能。

这里是一个简单的例子,演示了如何使用 strnstr 函数:

 #include <stdio.h>  #include <string.h>  int main() {  const char *s1 = "Hello, world!";  const char *s2 = "world";  size_t n = 12; // 只搜索 s1 的前 12 个字符  char *result = strnstr(s1, s2, n);  if (result != NULL) {  printf("Found '%s' in '%s' at position: %ld\n", s2, s1, result - s1);  } else {  printf("'%s' not found in '%s' (up to %zu characters).\n", s2, s1, n);  }  return 0;  } 

在这个例子中,strnstr 函数会在 s1 的前 12 个字符中搜索 s2("world")。因为 "world" 确实存在于这 12 个字符中,所以函数会返回指向 "world" 在 s1 中首次出现的位置的指针,并打印出该位置。如果 "world" 不在这 12 个字符中,或者 s1 本身少于 12 个字符,那么函数将返回 NULL。

模拟实现

strnstr函数的模拟实现可以有多种方式,下面我将提供两种基于不同策略的模拟实现,并加入assert语句来确保输入的有效性:

1. 朴素的循环实现

 #include <stdio.h>  #include <string.h>  #include <assert.h>  char *my_strnstr(const char *s1, const char *s2, size_t n) {  assert(s1 != NULL && s2 != NULL); // 确保s1和s2都不是NULL  if (n == 0) return NULL; // 如果n为0,则无法搜索,直接返回NULL  const char *p1 = s1;  const char *p2 = s2;  while (n--) {  if (*p2 == '\0') {  // 子串s2已结束,找到了匹配  return (char *)s1;  }  if (*p1 != *p2) {  // 当前字符不匹配,重置p2到s2的开头,p1移动到下一个字符  p2 = s2;  if (*p1 == '\0') {  // 主串s1已结束,没有找到匹配  return NULL;  }  p1++;  } else {  // 当前字符匹配,继续检查下一个字符  p1++;  p2++;  }  }  // 搜索完n个字符后仍未找到匹配  return NULL;  }  int main() {  const char *s1 = "Hello, world!";  const char *s2 = "world";  size_t n = strlen(s1); // 测试整个字符串  char *result = my_strnstr(s1, s2, n);  if (result != NULL) {  printf("Found '%s' in '%s' at position: %ld\n", s2, s1, result - s1);  } else {  printf("'%s' not found in '%s'.\n", s2, s1);  }  return 0;  } 

2. KMP(Knuth-Morris-Pratt)算法实现(简化版)

KMP算法是一种更高效的字符串匹配算法,但由于其实现相对复杂,这里仅提供一个简化的模拟版本。

 #include <stdio.h>  #include <string.h>  #include <assert.h>  // 简化的KMP算法实现(未完整实现KMP的所有优化)  char *my_strnstr_kmp(const char *s1, const char *s2, size_t n) {  assert(s1 != NULL && s2 != NULL);  if (n == 0) return NULL;  if (*s2 == '\0') return (char *)s1; // 如果s2为空字符串,则s1的任何位置都是匹配点  const char *p1 = s1;  const char *p2 = s2;  while (n--) {  if (*p1 == *p2) {  // 字符匹配,继续检查下一个字符  if (*p2 == '\0') {  // s2已结束,找到了匹配  return (char *)s1;  }  p1++;  p2++;  } else {  // 当前字符不匹配,重置p2到s2的开头,p1根据s2的某些性质移动(这里简化处理)  p2 = s2;  if (*p1 == '\0') {  // s1已结束,没有找到匹配  return NULL;  }  p1++;  }  }  // 搜索完n个字符后仍未找到匹配  return NULL;  }  int main() {  // ... 示例用法与上一个实现相同 ...  } 

注意:上面的KMP算法实现是一个简化的版本,它没有包含KMP算法中用于优化搜索过程的“部分匹配表”(Partial Match Table, PMT)或“失败函数”(Failure Function)。在实际应用中,完整的KMP算法会构建一个这样的表来加速搜索过程。
 

strstr和strnstr函数的区别

strstrstrnstr函数在C语言中用于字符串处理,但它们在功能和使用上有所不同。

  1. strstr函数:

    • 功能:用于在字符串str1中查找子字符串str2首次出现的位置。
    • 参数:接受两个字符串参数str1str2,其中str1是要被搜索的字符串,str2是要查找的子字符串。
    • 返回值:如果找到子字符串str2,则返回指向其在str1中首次出现的位置的指针;如果没有找到,则返回NULL
    • 特点:strstr函数会搜索整个str1字符串以查找str2,不考虑字符串的长度限制。
  2. strnstr函数:

    • 功能:与strstr类似,也是在字符串中查找子串,但允许指定要搜索的最大字符数。
    • 参数:除了接受两个字符串参数str1(主字符串)和str2(子字符串)外,还接受一个额外的参数n,表示在str1中参与搜索的最大字符数。
    • 返回值:与strstr相同,如果找到子字符串str2,则返回指向其在str1中首次出现的位置的指针;如果没有找到,则返回NULL
    • 特点:strnstr函数只会在str1的前n个字符中搜索str2,而不会搜索整个字符串。这在某些情况下可以提高效率,特别是当你知道子字符串不可能出现在主字符串的某个特定部分之后时。

总结来说,strstrstrnstr函数的主要区别在于是否允许指定搜索的最大字符数。如果你需要搜索整个字符串,可以使用strstr;如果你只想在字符串的某个特定部分中搜索,可以使用strnstr

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/4982.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【MHA】MySQL高可用MHA介绍2-安装,配置,要求与限制

目录 一 快速开始 简单故障转移 1 构建普通的复制环境 2 在host1-host4上安装MHA Node 3 在host4(manager_host)上安装MHA Manager 4 创建配置文件 5 检查SSH连接 6 检查复制配置 7 启动manager 8 检查manager状态 9 停止manager 10 测试主故障转移 11 下一步 二…

Vue3的新组件<Suspense>

在 Vue 3 中&#xff0c;你可以使用 <Suspense> 组件来包裹异步组件&#xff0c;从而提供一个更好的用户体验&#xff0c;包括在组件加载时的占位符和加载失败时的错误提示。<Suspense> 是 Vue 3 提供的一个新特性&#xff0c;允许你指定加载异步组件时的默认内容和…

IP路由安全:保护网络免受威胁

目录 前言 一 IPv4 协议及其安全性分析 IPv4 安全问题&#xff1a; 增强 IPv4 安全性的策略&#xff1a; 二 IPsec&#xff1a;增强 IP 通信安全 1.IPsec 工作原理&#xff1a; 2.IPsec 用例&#xff1a; 3.AH协议 AH 协议工作原理 AH 协议的工作原理高级概述 AH 协议…

深入理解 Python 中的 zip 函数

目录 一、zip 函数的基础用法 二、处理不等长的可迭代对象 三、使用 * 运算符进行解压 四、在并行迭代中使用 zip 五、zip 与字典 六、高级示例 同时遍历多个列表 使用 zip 创建字典的进阶用法 利用 zip 进行数据筛选 &#x1f680; 个人主页&#xff1a;xmp65535 &a…

【多态】有关多继承和菱形继承的多态

博主首页&#xff1a; 有趣的中国人 专栏首页&#xff1a; C进阶 其它专栏&#xff1a; C初阶 | 初阶数据结构 | Linux 博主会持续更新 本篇文章主要讲解 多继承和菱形继承的多态 的相关内容 文章目录 1. 回顾多态底层2. 抽象类2.1 概念2.2 接口继承和实现继承 3. 虚表所在…

数学小报4 - 三次方程的求根公式 Quadratic Formula

数学小报4 - 三次方程的求根公式 Quadratic Formula 0. 前言 完整内容同步发表于 https://blog.csdn.net/Mr_Azz/article/details/135443217 由于证明量过于巨大&#xff0c;部分证明简化&#xff0c;详情请见网址。 1. 思考 我们学习过一元二次方程的求根公式 x − b …

05.Vue2.x 数据代理

文章目录 Vue中的数据代理 Vue中的数据代理 !<!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-scale1.0" /><title>数…

Linux——web建立wordpress

下载 [rootnfs-server ~]# yum install php wget https://wordpress.org/latest.tar.gz解压 /var/www/html [rootnfs-server html]# tar -xzvf latest.tar.gz [rootnfs-server html]# rm latest.tar.gz授权 [rootnfs-server html]# chown -R www:www /var/www/html添加文件备…

利用kimi等大模型进行运维参数解析和调优

在运维时&#xff0c;经常遇到很多参数&#xff0c;有些参数不知道意义&#xff0c;知道意义的也有些不知道合理参考值是多少。利用kimi等大模型来当老司机&#xff0c;轻松解决运维难题。 例如在运维hive参数时&#xff0c;有些不知道作用&#xff0c;提示次如下 你的角色是…

windows ubuntu sed,awk,grep篇:7.sed 多行模式及循环

目录 46.读取下一行数据并附加到模式空间(命令 N) 47.打印多行模式中的第一行(命令 P) 48. 删除多行模式中的第一行(命令 D) 49.循环和分支(命令 b 和 :label 标签) 50.使用命令 t 进行循环 Sed 默认每次只处理一行数据&#xff0c;除非使用 H,G 或者 N 等命令创建多行模式&…

python学习笔记B-11:序列结构之列表--二维列表的遍历和生成式

二维列表的遍历方式&#xff0c;使用双层for循环&#xff0c;遍历索引号。 二维列表的生成式&#xff0c;也是使用类似双层循环的形式生成。 print("##初始化二维列表&#xff0c;每个元素就是1个列表") lst [["东方延续","太空军自然选择号舰长&qu…

释放Stable Diffusion 无限可能

最近在整理大语言模型的系列内容&#xff0c;Stable Diffusion 是我下一篇博客的主题。关注 Stable Diffusion&#xff0c;是因为它是目前最受欢迎和影响力最大的多模态生成模型之一。Stable Diffusion 于 2022 年 8 月发布&#xff0c;主要用于根据文本的描述产生详细图像&…

android 判断文件是否存在

在 Android 中&#xff0c;你可以使用 java.io.File 类来判断一个文件是否存在。下面是一个简单的示例代码&#xff1a; import java.io.File; public class FileChecker { public static boolean isFileExist(String filePath) { File file new File(fi…

基于SpringBoot+Vue笔记记录分享网站设计与实现

项目介绍&#xff1a; 信息数据从传统到当代&#xff0c;是一直在变革当中&#xff0c;突如其来的互联网让传统的信息管理看到了革命性的曙光&#xff0c;因为传统信息管理从时效性&#xff0c;还是安全性&#xff0c;还是可操作性等各个方面来讲&#xff0c;遇到了互联网时代…

uniApp设置和清除定时器

首先是在data中定义一个变量&#xff0c;用来存放定时器 data() {return {timer: null,} } 在适当的地方创建定时器 this.timer setInterval(() > {console.log(111); }, 10000) 在onHide或者是onUnload中销毁定时器&#xff0c;一般来说tabbar页面的切换会触发onHide&…

C语言 | Leetcode C语言题解之第50题Pow(x,n)

题目&#xff1a; 题解&#xff1a; double myPow(double x, int n){if(n 0 || x 1){return 1;}if(n < 0){return 1/(x*myPow(x,-(n1)));}if(n % 2 0){return myPow(x*x,n/2);}else{return x*myPow(x*x,(n - 1)/2);} }

【Jenkins】持续集成与交付 (三):有关报错解决(Jenkins (2.387.3) or higher required)

🟣【Jenkins】持续集成与交付 (三):有关报错解决Jenkins (2.387.3) or higher required 一、Jenkins主页报错二、安装Jenkins插件报错三、解决过程(解压替换jenkins.war)四、重新访问登录💖The Begin💖点点关注,收藏不迷路💖 一、Jenkins主页报错 New version …

Git Submodule 全流程使用指南

Git Submodule 是 Git 中用于管理子项目的强大功能。它允许我们将一个 Git 仓库作为另一个 Git 仓库的子模块进行管理&#xff0c;从而使项目结构更加清晰&#xff0c;代码维护更加方便。 本指南将详细讲解 Git Submodule 的创建、规划、更新、合并全流程的使用过程和操作步骤…

每天一个数据分析题(二百八十二)

积分表result中有A B C D四列&#xff0c;要求&#xff1a;1&#xff09;当A列值大于等于B列时&#xff0c;选择A列否则选择B列 2&#xff09;当C列值大于等于D列时&#xff0c;选择C列否则选择D列 用SQL语句实现正确的是&#xff1a;&#xff08; &#xff09; A. select ( w…

吴恩达2022机器学习专项课程(一)7.2 逻辑回归的简化成本函数

问题预览/关键词 本节课内容逻辑回归的损失函数简化之后的形式是&#xff1f;为什么可以简化&#xff1f;成本函数的通用形式是&#xff1f;逻辑回归成本函数的最终形式是&#xff1f;逻辑回归为什么用对数损失函数计算成本函数&#xff1f;为什么不直接给出逻辑回归损失函数的…