寻找字符串中的是否存在子字符串并返回相应的地址,这也是我今年九月份在南农参加烽火星空上笔试题时遇到的编程题。上一篇中介绍了CPrimerPlus的参考答案给出的解决方案,这里再和大家分享另一篇网上看到的解决方案(参考了ChinaUnix博客中net_liufeng的文章,博客地址:http://blog.chinaunix.net/uid-20384937-id-1953691.html)。网上找的这个方案有个优点,他不是直接给出代码,而是先阐述了他的思路,然后再给出代码。这对于读者理解还是很有帮助的,感谢这位前辈了!下面先摘抄一下作者的思路:
从母字符串第一个字符开始,和子串比较,如果第一个字符和子串的第一个相等,就比较之后的子串的长度个字符,如果中间有不同的,就跳出循环,这时候i不是子串的长度,直到第一次找到全部匹配的时候,返回这时候母串的那个指针,这个实现应该也是最原始的了。。
char *string_in(char *str, char *sub_str){ int i = 0; size_t len = strlen(sub_str); //记录子字符串的长度 if ( len == 0 ) return NULL; //如果子字符串长度为0,返回NULL while ( *str != '\0' ) //对于母字符串,从头至尾挨个字符进行比较和判断 { if ( *str == *sub_str ) //如果找到了和子字符串首元素相同的字符 { for ( i = 1; i < len; i++) //循环判断母字符串中连续有多少个字符能和子字符串相同,记录为i个(这个循环里面并没有让母字符和子字符的指针后移,如果发现和子字符串不完全匹配的话,可能会重复计算,尤其是子字符串的前很多位都和母字符串的一连串字符相同但最后却又不同时) { if ( *(str + i) != *(sub_str + i) ) break; } } if ( i == len ) //如果连续相同的字符个数i和子字符串长度相同,则说明找到了子字符串了,返回地址return str; else str++; //母字符指针后移,准备继续比较 }}
函数实际测试时,出现了个小问题。在子字符串存在的情况下,确实能准确返回子字符串首次出现时所在的位置;而在字符串不存在于母字符串时,并没有返回NULL,也就是没有给出明确的回应说找不到子字符串。根据作者的思路,想到个小主意,就是添加一个flag标志,如果找到了就改变flag值(然后返回相应的地址,此情况下作用不大),如果没找到就不变,这样在把整个母字符串的字符从头到尾都检查一遍并且没有发现子字符串时,可以判断flag没有改变而返回NULL。
其实就是一个问题:在整个母字符串中字符的挨个比较过程中,一旦找到了匹配的子字符串就返回相应地址就完事(再往后的母字符串中的字符就不管了);找遍了都找不到匹配的子字符串而停止寻找后,要能意识到找不到子字符这件事,要返回结论(臣妾找不到啊)。(其实想想也蛮有意思的,比方说在生活中,老板让你在众多文件中帮他找一个文件,找的时候找到了你就告诉他(剩下的文件就不用管了),找遍了都找不到的话,也不能一走了之吧^_^,还得跟老板说一下:这个真没有~)
下面是修改后的代码(橙色标出):
char *string_in(char *str, char *sub_str){ int i = 0, flag = 0; size_t len = strlen(sub_str); if ( len == 0 ) return NULL; while ( *str != '\0' ) { if ( *str == *sub_str ) { for ( i = 1; i < len; i++) { if ( *(str + i) != *(sub_str + i) ) break; } } if ( i == len ) { flag = 1; return str; } else str++; } if(flag == 0) return NULL;}
运行结果就不贴图了。
到此为止两种方案都介绍完了,我也算是加深了些理解吧,感觉自己编还是有点难度的(自己实力太菜了)。两种方案都是循环着挨个来处理母字符串中的字符,每次循环只处理一个字符。只是第一种用死循环,循环中考虑清楚了各种不同的情况返回不同的值从而退出循环然后退出函数;第二种把母字符串中的字符遍历一遍,找到子字符串或者找到尾都找不到子字符串来返回不同的结果然后退出函数;总体来看的话,我觉得第二种方案的思路更容易理解一些,但第一种更加高效和有“灵感”一些吧。