見出し画像

C言語-#7.str*str

はおはお。

strstr関数は文字列から指定した文字列があるかチェックする関数です。ここでは先に公開した比較関数を使用して実装します。


strnstr

char* strnstr(const char* s, const char* ptn, int ptnlen) {
  int ch1 = *ptn;                   // 最初の文字
  int ch2 = *(ptn + (ptnlen - 1));  // 最後の文字

  int i;

  // s の長さチェック
  for (i = 0; i < ptnlen; ++i) {
    if (s[i] == '\0') {
      return 0;  // s が ptn よりも短い
    }
  }

  s = s + (ptnlen - 1);  // 位置を ptn 分進める

  for (;;) {
    // 最後と最初が一致
    if (*s == ch2 && *(s - (ptnlen - 1)) == ch1) {
      // ※最初と最後は判定済み
      if (strncmp(s - (ptnlen - 1) + 1, ptn + 1, ptnlen - 2) == 0) {
        return (char*)(s - (ptnlen - 1));
      }
    }
    // null がきたので終了
    else if (*s == '\0') {
      return 0;
    }

    ++s;
  }

  //return 0;
}

strnstr関数は s から長さ ptnlenptn列に見つかるまで走査します。
文字列を比較するときはパターンの先頭と終端を先に比較して一致していれば全体を比較します。少しでも比較回数を減らすためのちょっとした方法です。
strncmpの引数がややこしくなってますが、123456 とあった場合に 2345 を比較する式です。これはコメントにもありますが、すでに先頭と終端は比較済みだからです。
あと、(s - (ptnlen - 1)) はちょっと冗長かもですね。

strstr

char* strstr(const char* s, const char* ptn) {
  return strstrs, ptn, strlen(ptn));
}

パターンの長さを strnstr に渡すだけですね。

けっこー速度には自信あるんですけどどうなんでしょ。この関数を使って grepソフトを作成しました。古いソフトなのでちゃんと動くかな……。

おまけ strstr(BM法)

char* strstr(const char* s, const char* ptn)
{
	// 文字列 or パターンがあるときだけー
/*	if (!s && !ptn) {
		return -1; // 文字がないよー
	}
*/
	
	int s_len	= strlen(s);
	int ptn_len	= strlen(ptn);
	
	// テキストとパターンの不一致が見つかったときに
	// どれだけずらすかを示す表\_
	int skip[256];
	
	// 変数 i は注目しているテキストの位置、
	// 変数 j は注目しているパターンの位置を表すポインタ
	int	i, j;
	
	// テキストよりパターンのほうが長いので見つからないです
	if (s_len < ptn_len) {
		return 0;
	}
	
	// 表 skip を作成する
	for (i = 0; i < 256; ++i) {
		skip[i] = ptn_len;
	}
	for (i = 0; i < ptn_len - 1; ++i) {
		skip[(uint8_t)ptn[i]] = ptn_len - i - 1;
	}
	
	// ポインタを初期化する。パターンの後ろから前に向かって比較するので、
	// パターンの長さ -1 に初期化する。
	i = ptn_len - 1;
	
	// テキストの最後尾に行き当たるまで繰り返す
	while (i < s_len) {
		// ポインタ j をパターンの最後の文字を指すようにする
		j = ptn_len - 1;
		
		// テキストとパターンが一致する間、繰り返す
		while (s[i] == ptn[j]) {
			// 最初の文字まで一致したら、探索は成功である
			if (j == 0) {
				return s + i;
			}
			
			// ポインタ i と j をそれぞれ1文字分戻す
			i--;
			j--;
		}
		
		// 一致しなかったので、パターンをずらす
		if (skip[(uint8_t)s[i]] > (ptn_len - j)) {
			i += skip[(uint8_t)s[i]];
		}
		else {
			i += ptn_len - j;
		}
	}
	
	
	// 結局見つからなかった
	return 0;
}

悉く書を信ずれば則ち書無きに如かず