このページでは、C言語の標準関数である strstr
関数の使い方を解説していきます!
strstr
関数を利用することで、文字列検索をお気軽に実行することができます。
また、strstr
は後述するように、基本的には最初に見つけたパターンの位置のみを返却する関数ですが、使い方を工夫すれば全パターン検索等のいろんな文字列検索を実現することができます。
このページでは、strstr
関数自体の説明だけでなく、この strstr
関数のいろんな使い方についても解説していきたいと思います!
ちなみに、strstr
関数を使わなくても自力で文字列検索を行うことも可能です。やり方を知りたい方は、下記のページを参考にしていただければと思います。
Contents
strstr
関数
strstr
関数はある文字列(テキスト)の中から指定した文字列(パターン)を検索する関数です。
strstr
関数の定義
strstr
関数の定義ファイル、書式は下記の通りです。
#include <string.h>
char* strstr(const char* text, const char* pattern);
スポンサーリンク
strstr
関数の引数
strstr
関数の第1引数 text
にはテキスト(探索 “先” になる文字列)が格納された配列やメモリのアドレスを、第2引数 pattern
にはパターン(探索したい文字列)が格納された配列やメモリのアドレスを指定します。
strstr
関数の戻り値
テキスト text
の中からパターン pattern
が見つかった場合、その pattern
が見つかった位置をアドレスとして返却します。
見つからなかった場合は NULL
を返却します。
strstr
関数の詳細
strstr
関数では text
の指すテキストの中から pattern
の指すパターンの検索が行われます。
text
の指すテキストの中から pattern
の指すパターンが見つかった場合、パターンが見つかった位置のアドレスが返却されます。パターンが複数文字の場合、先頭の文字の位置のアドレスが返却されます。
例えば下の図の場合、青枠で囲った text
の 'A'
の文字の位置のアドレスが返却されます。もっと具体的にいうと、アドレス &text[2]
が返却されます。
また、この strstr
関数における文字列検索はテキストの先頭から順に行われます。そして、最初に見つけたパターンの位置のアドレスが返却されます。ですので、テキストの中にパターンが複数存在する場合、一番テキストの先頭側に存在するパターンの位置のアドレスが返却されることになります。
さらに、strstr
関数における文字列検索は、”大文字小文字を区別して” 行われます。
スポンサーリンク
strstr
関数のいろんな使い方
ここまでの解説だと、strstr
関数では “最初のパターンしか見つけられない” & “取得できるのはアドレスのみ” のようにも思えますね!実際そうなんですが…。
ただ、strstr
関数の呼び出し側を工夫することで、次のようにいろんな文字列検索が可能になります。
パターンが存在するかどうかを判断する
まずは strstr
関数の基本的な使い方を紹介しておきます。
おそらく strstr
関数の最も基本的な使い方は、strstr
関数の戻り値から “テキストの中にパターンが存在するかどうかを判断する” という使い方だと思います。
strstr
関数はパターンが見つからなかった場合に NULL
を返却しますので、strstr
関数の戻り値が NULL
であるかどうかでパターンがテキストの中に存在するかどうかを判断することができます。
パターンが存在するかどうかを判断するときの strstr
関数の利用例は下記のようになります。
#include <stdio.h>
#include <string.h>
int main(void) {
char text[] = "aabaabbaab";
char pattern[] = "ab";
char *find; /* パターンの存在する位置のアドレス格納用 */
/* textからpatternを検索 */
find = strstr(text, pattern);
if (find != NULL) {
/* パターンが存在する場合 */
printf("パターンは%pに存在します\n", find);
} else {
/* パターンが存在しない場合 */
printf("パターンは存在しません\n");
}
return 0;
}
何文字目にパターンが存在するかを取得する
文字列検索の結果として、アドレスではなく、テキストの何文字目にパターンが存在するかを知りたい場合があります。
strstr
関数の戻り値はパターンが存在する位置のアドレスですが、この “戻り値のアドレスとテキストの先頭アドレス” との差を取ることで、先頭からパターンが存在する位置が何文字分離れているか(char
型の変数いくつ分離れているか)を計算することができます。
ですので、文字列の先頭を1文字目と考えるでのであれば、戻り値のアドレス - テキストの先頭アドレス + 1
文字目にパターンが存在することになります。もし文字列の先頭を第 0
文字として考えるのであれば、第 戻り値のアドレス - テキストの先頭アドレス
文字にパターンが存在することになります。
下記は何文字目にパターンが存在するかを取得する時の strstr
関数の使い方の例になります(文字列の先頭を第 0
文字として考えています)。
#include <stdio.h>
#include <string.h>
int main(void) {
char text[] = "aabaabbaab";
char pattern[] = "ab";
char *find; /* パターンの存在する位置のアドレス格納用 */
long pos; /* パターンが存在する位置格納用 */
/* textからpatternを検索 */
find = strstr(text, pattern);
if (find != NULL) {
/* パターンが存在する場合 */
/* アドレスの差から先頭から何文字離れた位置にパターンが存在するのかを算出 */
pos = find - text;
printf("パターンは第%ld文字に存在します\n", pos);
} else {
/* パターンが存在しない場合 */
printf("パターンは存在しません\n");
}
return 0;
}
スポンサーリンク
パターンを全て検索する
パターンの全検索は、strstr
関数の第1引数に指定するテキストのアドレスを、パターンの存在する位置の次の位置のものに変化させていくことで実現することができます。
第1引数にテキストの先頭のアドレスを指定して strstr
関数を実行することで、”最初に見つけた” 位置のアドレスが戻り値として取得できます。さらにパターンを検索したい場合は、次はその 戻り値のアドレス + 1
を第1引数に指定して strstr
関数を実行します。
これにより、先ほど見つけたパターンをスキップした状態で文字列検索が行われるので、次の strstr
関数の戻り値である “最初に見つけた” 位置のアドレスは、テキストの先頭から考えると “2番目に見つけた” 位置のアドレスになります。
あとはこれを strstr
関数が NULL
を返却するまでループすれば、テキストの中からパターンを全検索することが可能です。
パターンを全検索する時の strstr
関数の利用例は下記のようになります。
#include <stdio.h>
#include <string.h>
int main(void) {
char text[] = "aabaabbaab";
char pattern[] = "ab";
char *start; /* 検索開始する位置のアドレス格納用 */
char *find; /* パターンの存在する位置のアドレス用 */
/* テキストの先頭アドレスをtextの先頭にセット */
start = text;
/* パターンが見つかっている間strstrを繰り返し実行 */
do {
/* startから始まる文字列からpatternを検索 */
find = strstr(start, pattern);
if (find != NULL) {
/* patternが見つかった場合 */
/* patternが存在する位置のアドレスを表示 */
printf("%pにパターンが存在します\n", find);
/* テキストの先頭を見つけたパターンの位置の次の文字とする */
start = find + 1;
}
} while (find != NULL);
return 0;
}
大文字小文字を区別せずに検索する
次は大文字と小文字を区別せずに検索したい場合の strstr
関数の使い方について解説していきます。
strstr
関数の仕様が “大文字と小文字を区別する” なので、strstr
関数が大文字と小文字を区別して検索してしまうのはどうしようもないです。
ですので、大文字と小文字を区別せずに strstr
関数で文字列検索を行いたいのであれば、テキストとパターンを大文字 or 小文字に統一してから strstr
関数を実行するしか方法はないと思います。
大文字小文字変換する方法については下記ページで解説していますので、こちらを参考にしていただければと思います。
C言語での大文字 ⇔ 小文字変換の方法を解説ただ、文字列検索を行うためにテキストやパターンの文字列が変更されてしまうのもイマイチなので、大文字 or 小文字に変換した文字列は別の配列や malloc
関数で別途確保したメモリに格納するようにした方が良いと思います。
大文字小文字区別せずに検索するときの strstr
関数の利用例は下記のようになります。テキストとパターンを全て小文字に変換してから strstr
関数実行しています(小文字に変換した結果は別途 malloc
関数で確保したメモリに格納しているので、元々のテキストとパターンは変更されていません)。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(void) {
char text[] = "aabaabbaab";
char pattern[] = "aB";
char *find; /* パターンの存在する位置のアドレス格納用 */
char *lower_text;
char *lower_pattern;
size_t text_len;
size_t pattern_len;
int i;
char c;
/* 文字数を取得 */
text_len = strlen(text);
pattern_len = strlen(pattern);
/* 文字数+1(NULL文字)のサイズ分メモリを取得(エラー処理省略) */
lower_text = (char*)malloc(sizeof(text_len + 1));
lower_pattern = (char*)malloc(sizeof(pattern_len + 1));
/* テキストの全文字を小文字に変換 */
for (i = 0; i < text_len; i++) {
c = text[i];
/* cが大文字の場合、小文字に変換する */
if('A' <= c && c <= 'Z'){
c = c + ('a' - 'A');
}
lower_text[i] = c;
}
/* 文字列の最後にNULL文字を付加する */
lower_text[text_len] = '\0';
/* パターンの全文字を小文字に変換 */
for (i = 0; i < pattern_len; i++) {
c = pattern[i];
/* cが大文字の場合、小文字に変換する */
if('A' <= c && c <= 'Z'){
c = c + ('a' - 'A');
}
lower_pattern[i] = c;
}
/* 文字列の最後にNULL文字を付加する */
lower_pattern[pattern_len] = '\0';
/* lower_textからlower_patternを検索 */
find = strstr(lower_text, lower_pattern);
if (find != NULL) {
/* パターンが存在する場合 */
printf("パターンは%pに存在します\n", find);
} else {
/* パターンが存在しない場合 */
printf("パターンは存在しません\n");
}
/* mallocで確保したメモリを解放 */
free(lower_text);
free(lower_pattern);
return 0;
}
少し strstr
関数の使い方とは趣旨が異なるのですが、実は大文字小文字を区別せずに検索する関数として strcasestr
が利用できる場合があります。
strcasestr
の引数等の使い方は strstr
関数と全く同じで、異なるのは検索時に大文字小文字を区別しないという点のみになります。
ただ strcasestr
関数は “非標準” 関数なので、環境によっては使用できない可能性もあるので注意してください。
strcasestr
関数を利用したくない、そもそも利用できる環境でない方は、この節で解説したように strstr
関数を利用すると良いと思います。
まとめ
このページではC言語の strstr
関数について解説しました!
strstr
関数を利用することで、文字列検索を手軽に行うことができます。
また、基本的には strstr
は “大文字小文字を区別して検索する” & “最初に見つけたパターンの位置のアドレスを返却する” 関数ですが、呼び出し側の処理を工夫することで、パターンの全検索や大文字小文字を区別しない検索も実現することが可能です!
今回は strstr
関数に注目して解説しましたが、他の標準関数も使い方を工夫すれば様々な機能を実現することができます。関数自体の動作を理解するだけでなく、その関数をうまく使いこなす方法についてもぜひ考えてみてください!