【C言語】strstr関数の使い方(文字列を検索する関数)

strstr関数の使い方の解説ページアイキャッチ

このページでは、C言語の標準関数である strstr 関数の使い方を解説していきます!

strstr 関数を利用することで、文字列検索をお気軽に実行することができます。

また、strstr は後述するように、基本的には最初に見つけたパターンの位置のみを返却する関数ですが、使い方を工夫すれば全パターン検索等のいろんな文字列検索を実現することができます。

このページでは、strstr 関数自体の説明だけでなく、この strstr 関数のいろんな使い方についても解説していきたいと思います!

ちなみに、strstr 関数を使わなくても自力で文字列検索を行うことも可能です。やり方を知りたい方は、下記のページを参考にしていただければと思います。

力まかせ法の解説ページアイキャッチ力まかせ法(単純な文字列検索方法)について解説(C言語サンプル付き)

strstr 関数

strstr 関数はある文字列(テキスト)の中から指定した文字列(パターン)を検索する関数です。

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 関数の最も基本的な使い方は、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引数に指定するテキストのアドレスを、パターンの存在する位置の次の位置のものに変化させていくことで実現することができます。

パターンを全検索するときのstrstr関数の第1引数の指定の仕方を示した図

第1引数にテキストの先頭のアドレスを指定して strstr 関数を実行することで、”最初に見つけた” 位置のアドレスが戻り値として取得できます。さらにパターンを検索したい場合は、次はその 戻り値のアドレス + 1 を第1引数に指定して strstr 関数を実行します。

これにより、先ほど見つけたパターンをスキップした状態で文字列検索が行われるので、次の strstr 関数の戻り値である “最初に見つけた” 位置のアドレスは、テキストの先頭から考えると “2番目に見つけた” 位置のアドレスになります。

strstr関数の第1引数に指定したアドレス以前の文字がスキップされて検索が行われる様子

あとはこれを 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 関数に注目して解説しましたが、他の標準関数も使い方を工夫すれば様々な機能を実現することができます。関数自体の動作を理解するだけでなく、その関数をうまく使いこなす方法についてもぜひ考えてみてください!

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です