一、可以用三种方式解决子串问题:函数strstr、指针、子串出现的首个位置。
先举几个例子来说明一下什么样的字符串可以称作子串(区分大小写,假设字符串数组长度为20):
令str1[20]="dou LUO da lu";
1.str2[20]="da l";是子串;
2.str2[20]="dal";不是子串;
3.str2[20]="LUO";是子串;
4.str2[20]="Luo";不是子串;
5.str2[20]="dou da";不是子串。
二、使用函数strstr判断str1中是否包含str2
1.strstr函数在库函数"string.h"中,它的使用方法是strstr(str1,str2),函数得到的是地址,并不是数据内容,所以要用指针变量来接收;
2.如果判断str2是子串,就会返回str1中首次出现str2加上其后的内容,否则返回null;
举个例子:str1[20]="dou LUO da lu"; str2[20]="da l";那么strstr(str1,str2)返回的内容是"da lu";
3.运行代码和结果(codeblocks和手机程序C Compiler可运行)
#include<stdio.h>
#include<string.h>
void main()
{
char str1[20],str2[20];
char *str;//设置指针变量来存储子串在母串中的地址
printf("请输入被查找字符串str1:");
scanf("%s",str1);
printf("要查找的字符串str2:");
scanf("%s",str2);
str=strstr(str1,str2);
if(str)//判断str是否为空,不能写成str!=null
{
printf("str2是子串:%s\n",str);
}
else
{
printf("str2不是子串:%s\n",str);
}
}
注意代码中进行比较的两个字符串是运行程序后手动输入的,用的scanf函数获取,所以字符串得是连续的,若有空格则会自动将前两个字符串分别赋值给str1和str2,至于第二个空格及以后有多少内容都不管,看结果就知道了:
所以可以把原代码中的scanf函数改成:
printf("请分别输入字符串str1和str2,用空格隔开:");
scanf("%s%s",str1,str2);
运行结果:
4.如果不用输入的方式,直接在程序中给定两个字符串的话,可以有多个空格,代码如下:
#include<stdio.h>
#include<string.h>
int main()
{
char str1[20]="dou LUO da lu";
char str2[20]=" LUO d";//注意必须写成数组型,一定要带下标[N]!!
char *str;
str=strstr(str1,str2);
printf("根据结果自行判断:%s\n",str);
}
关于strstr函数的详细定义可以参看https://www.runoob.com/cprogramming/c-function-strstr.html
三、利用指针来判断是否为子串
1.基本思路:利用指针p依次遍历str1,每遍历一位字符便将之与str2的首地址值q进行比较,如果相同则两个指针同时自增,然后依次向后比较,若不同则指针p往下移动一位,再与str2的首地址值q进行比较,循环往复,直到str2遍历完(即确定str2为子串),或者str1遍历完。
①外层循环:for(p=str1;p;p++)意思是从首地址开始,只要p不等于'0',则执行内嵌语句块,然后自增;
②内层循环:for(q=str2;*p==*q&&*q;p++,q++); 意思是从首地址开始,判断两个字符串的值是否相等且str2是否遍历完,因为for语句后面有分号,表示它没有内嵌语句,所以若判断为真则执行第三表达式,判断为假则退出内层循环,执行外循中内嵌的其他语句,然后再执行外层循环,接着再从头开始执行内层循环。
关于for循环的详细用法可参看http://c.biancheng.net/cpp/html/45.html
2.指针初始化与赋值需要注意,在c语言里没有总体处理一个字符串的机制。例如:
①char *p="asdfgh"; 初始化,指针p指向字符串首地址
②char *p; p="asdfgh"; 赋值,指针p指向字符串首地址
③char a[10],*p; p=a; 赋值,数组的变量名a表示该数组的首地址
④int a=1; int b[10]; int *p=&a; int *q=b; int *w=&b[0];这些指针初始化都是正确的
总的来说,指针p指向地址,而*p指向数据,详细了解指针可参看https://www.cnblogs.com/mfrbuaa/p/3756342.html
3.代码及运行情况:
#include<stdio.h>
int main()
{
char str1[20]="sadadadf";
char str2[20]="adf";
char *p=str1,*q=str2;//指针初始化,赋以首地址
int flag=0;//设置一个变量来判断是否为子串
for(p=str1;*p;p++)//将str1的首地址赋给p;第二个表达式判断字符是否为‘0’;
{
for(q=str2;*p==*q&&*q;p++,q++);//注意这里有分号!!这个for语句没有内嵌代码
if(!*q)//如果str2遍历完,此时*q的值一定是默认值‘0’,那么!*q为真
{
flag=1;
break;//得到想要的答案之后就跳出循环
}
}
if(flag==1) printf("是");
else puts("否");//判断后执行紧邻第一句,可以不加大括号{},puts()和printf()都可以输出
}
啰嗦几句:最开始用while语句写的代码,发现运行结果不对,调试之后还是for循环好用点,但这个程序还是有个小问题,如果str1="sadadf"; str2="adf"; 运行结果显示“否”,不正确,若str2="df",运行结果为“是”,是对的,那是因为内层循环比较到str1[3]和str2[2]时,前者是‘a’,后者是‘f’,这时候内存循环结束,外层循环执行第三表达式p++,这时候*p='d',再执行内层循环也得不到想要的结果了,我就想着是不是可以再增加一个内层循环,可是呢,对于现在这两个字符串来说,结果是对了,可是如果str1="sadadadf";结果又会出错,还是跟之前一样的问题,嗯~一般情况下还是好使的,不过下面这种不用函数和指针的方法更好,没有这些问题!
四、不用函数和指针,返回子串在母串中出现的首位置
1.用getchar()来获取一个个字符,依次存入数组当中;
getchar()的具体用法可参见https://www.runoob.com/cprogramming/c-function-getchar.html
2.用while循环将获得的字符依次存入数组中:while((c=getchar())!='\n') a[num++]=c;
①如果获得的字符不是回车符,就存入数组a中,包括空格。注意while循环后没有大括号{}或分号;时,则紧邻的第一句为其循环体;
②num=0;则(num++)=0,num=1,所以是从a[0]开始存,存到最后一个字符a[num-1],循环结束。
3.进行判断,如果是子串,返回并输出子串在母串中出现的首位置,否则什么都不返回。(同while一样,若for和if后面没有分号和大括号,则紧跟的第一句为其循环体)
首先,外层循环先依次判断母串元素a[i]与要查找串的首位值b[0]是否相等,如果相等则进行下一步循环与比较,内循表达式如下:
if(a[i]==b[0])
{
for(j=1;j<lb;j++)
if(a[i+j]!=b[j])
break;
if(j==lb)
{
printf("%d\n",i+1);
}
}
因为前面已经判断了a[i]与b[0]相等,所以将数组下标分别下移一位,再比较,每比较一次就判断是否相等,如果相等,下标下移并比较;如果不相等则退出内层循环,判断j与lb是否相等(此时肯定不相等的,只是遵从顺序执行规则,要判断一次),再回到if(a[i]==b[0])语句,此时a的下标已经变成了i+j,即i=i+j,将这一位与b的首位再比较,若相等则循环比较,若不相等则退回到外层循环。
如果比较之后确定为子串,那么子串一定遍历完,此时jj与lb一定相等。
4.代码及运行情况:
#include<stdio.h>
#include<string.h>
int main()
{
int i,j,la,lb,num=0;
char c,a[120],b[100];
printf("请输入母串:");
while((c=getchar())!='\n')//while循环后没有大括号{}或分号;时,则紧邻的第一句为其循环体
a[num++]=c;//用数组a存储被查找字符串
num=0;//num一定要重新赋值为0,否则数组b的值就不是从首地址开始
printf("输入要查找的字符串:");
while((c=getchar())!='\n')
b[num++]=c;//用数组b存储要查找的字符串
la=strlen(a);
lb=strlen(b);
for(i=0;(la-i)>=lb;i++)//如果遍历到i还没行,将剩下的串长与要查找的串长相比,若还大,接着循环,若小,就不用再比较了
{
if(a[i]==b[0])
{
for(j=1;j<lb;j++)
if(a[i+j]!=b[j])//for后面没有分号和大括号,所以紧跟的第一句为其循环体
break;//if后面没有分号和大括号,所以紧跟的第一句为其循环体
if(j==lb)
{
printf("子串出现的首位置是母串第%d位\n",i+1);
break;
}
}
}
}
文中三种思路来源于文章https://blog.csdn.net/fakine/article/details/7517417/
写在最后:每次代码看懂了,复制能够运行,然后自己码一遍,结果运行没问题,但就是结果出了问题,前前后后仔细对比,就是不知道哪里错了,感觉都是对的啊,每次到最后找到问题所在时,基本上都是咪咪小问题被忽略了,就造成了大问题,比如多个或少个分号啊空格,或者数据类型搞的不对、少个简单但很关键的语句等,好的习惯需要更多训练。
版权归原作者 一只傲娇孤鹜 所有, 如有侵权,请联系我们删除。