C語言中的字串函式有如下這些
- 獲取字串長度
- strlen
- 長度不受限制的字串函式
- strcpy
- strcat
- strcmp
- 長度受限制的字串函式
- strncpy
- strncat
- strncmp
- 字串查詢
- strstr
- strtok
- 錯誤資訊報告
- strerror
字串查詢
strstr
還是一樣,先看看如何使用它,對吧哈哈哈。
int main()
{
char* p1 = "abcdef";
char* p2 = "def";
// 在abcdef中找找def,找到的話返回它的地址,找不到返回空指標
char* rest = strstr(p1, p2);
if (rest == NULL)
{
printf("子串不存在\n");
}
else
{
printf("%s\n", rest);
}
return 0;
}
老規矩,我們還是看看文件是怎樣說的,如下
char * strstr ( const char * str1, const char * str2 );
Returns a pointer to the first occurrence of str2 in str1, or a null pointer if str2 is not part of str1.
返回一個指標,它指向str2,該str2是在str1中第一次出現的str2,或者一個空指標,如果str2不是str1的子串。
The matching process does not include the terminating null-characters, but it stops there.
這個匹配過程不包含'\0',但是它會停在那裡。
實現
我們需要想想,它是如何實現字串查詢的?
有兩個字串,str1和str2,在str1中查詢str2,那麼我們需要兩個指標p1
和p2
來進行,p1
指向str1開頭,p2
指向str2開頭,然後獲取字元一一比較。
如果*p2
為'\0'
那麼說明str2是空字串,不能進行比較,就返回p1
,即返回str1的地址。
如果*p2
不為'\0'
,就說明不是空字串,同時,也要判斷*p1
是否為空字串,不是就可以進行查詢。
查詢的話,此時,我就通過*p1 == *p2
判斷*p1
等於*p2
?等於就都進行偏移,即p1++
和p2++
,然後繼續判斷,這裡就成了一個迴圈,一直迴圈,直到它們兩個不相等跳出迴圈。跳出迴圈後,如果*p2
等於'\0'
,說明已經查詢到了,直接返回p2就好。如果*p2
不等於'\0'
,那麼就p1就進行偏移,往後移動,繼續判斷,這裡也形成了一個迴圈。到這裡,基本的邏輯就這樣了。
下面看看程式碼的實現。
斷言指標不為空是個好習慣~
char* my_strstr(const char* p1, const char* p2)
{
// 保證指標的有效性,所以assert
assert(p1 != NULL);
assert(p2 != NULL);
// 如果p2是空字串,那就比不了
if (*p2 == '\0')
{
printf("空字串比不了,返回p1");
return p1;
}
// 真正的查詢實現
while (*p1) // 判斷*p1是'\0'嗎?不是就可以查詢
{
//while (*p1 == *p2) // 判斷*p1等於*p2?等於就都進行偏移
while ((*p1 != '\0') && (*p2 != '\0') && (*p1 == *p2) ) // 繼續完善,*p1,*p2都不能是\0,遇到\0就結束了,沒東西可比了
{
p1++;
p2++;
}
if (*p2 == '\0')
{
// 說明匹配到了
return p2;
}
p1++; // 不等於,那麼p1往後偏移
}
}
是的,到這裡還沒有結束,上面看似可以進行匹配了,但是程式碼還是有問題,比如遇到這種情況的時候
int mian()
{
char* p1 = "abbbcdef";
char* p2 = "bbc";
char* rest = my_strstr(p1, p2);
return 0;
}
第一個字串的第一個字元a與第二個字串的第一個字元b進行比較,發現不相等,那麼p1就進行偏移,p1往後移動
此時b與b相比,相等,那麼p1和p2都進行偏移,都往後移動
還是b與b相比,相等,繼續偏移
此時b與c相比,不相等,那麼p1進行偏移,此時p1指向的就是第五個字元c了,後面繼續比較下去,肯定都不相等,也就是說找不到bbc,但是第一個字串裡明明有bbc,就是找不到,這就是會出現的問題。
那如何解決這個問題?
我們知道,如果可以讓p1重新回去第二個字元的位置開始比較,那麼肯定就能夠找到bbc,但是上面的程式碼中,使p1發生改變了,p1不知道第二個字元b的位置,直接從第五個字元c的位置開始了。
所以,要解決的話,我們就需要一個變數記錄從哪個位置開始匹配的,然後我們不要去改動p1,同時保險起見,也不要改動p2,那麼我們就可以搞多兩個指標變數s1
和s2
,作為p1
和p2
的拷貝,對這兩個變數進行操作,就OK了~然後搞多一個變數current
,作為當前需要移動的指標。
char* my_strstr(const char* p1, const char* p2)
{
// 保證指標的有效性,所以assert
assert(p1 != NULL);
assert(p2 != NULL);
// p1,p2不要往後動
// 需要一個變數記錄從哪個位置開始匹配
//char* s1 = p1; // 這裡賦值無所謂,就給NULL好了
char* s1 = NULL;
char* s2 = NULL;
char* current = (char*)p1; // 這裡強制型別轉換,因為p1是const修飾,賦值給了char*這個沒有保護的,所以強轉下,不然會報警告
// 如果p2是空字串,那就比不了
if (*p2 == '\0')
{
printf("空字串比不了,返回p1");
return (char*)p1;
}
// 真正的查詢實現
while (*current) // 判斷*current是'\0'嗎?不是就可以查詢
{
s1 = current;
s2 = (char*)p2;
while ((*s1 != '\0') && (*s2 != '\0') && (*s1 == *s2))
{
s1++;
s2++;
}
if (*s2 == '\0')
{
// 說明匹配到了
return current; // 返回子串地址
}
if (*s1 == '\0')
{
// 如果子串比較長,那麼肯定是找不到的
return NULL;
}
current++; // 不等於,那麼current往後偏移
}
return NULL; //找不到子串
}