C語言-字串函式的實現(五)之strstr

god23bin發表於2021-04-21

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;
}

老規矩,我們還是看看文件是怎樣說的,如下

strstr文件

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,那麼我們需要兩個指標p1p2來進行,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,那麼我們就可以搞多兩個指標變數s1s2,作為p1p2的拷貝,對這兩個變數進行操作,就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;	//找不到子串
}

相關文章