C言語の文字列の検索方法を解説: strchr, strstr, 自力で検索

346, 2021-11-15

目次

C言語の文字列の検索方法

C言語では文字列を扱うことができます。
この記事ではC言語の文字列から文字や文字列を検索する方法を解説します。

文字列の検索はさまざまなプログラムで使われる基本的なロジックです。
C言語でもそのロジックのための関数は提供されています。
それはstrchr()strstr()などです。

この記事では具体的に↓のコンテンツを見ていきます。

  • C言語の文字列の検索の原理

  • strchrの使い方

  • strstrの使い方

  • 自力で文字列を検索する

C言語の文字列の検索の原理

C言語の文字列検索の基本的な原理を解説します。
strchr()などの文字をキーにして文字列から検索する関数ですが、これはどういう原理なのか?
C言語の文字列は文字の集合のお尻をナル文字でふさいだものです。

つまりこの文字列から特定の文字を検索する場合は、その文字列を先頭から順に見ていきます
そしてヒットした文字があったら、その場所のポインタを返します。
検索がナル文字まで到達したら、その文字にヒットする文字は含まれていないということになります。
その場合はNULLポインタを返します。
これが文字検索の原理です。

いっぽうstrstr()などの文字列をキーにして文字列から検索する関数です。
これは同様に文字列の先頭から順に見て行って、現在のインデックスを基点にターゲットの文字列を比較します。
そしてそのターゲットの文字列にヒットする文字列が見つかったら、その場所のポインタを返します。
同様に検索がナル文字まで到達したら、ターゲットの文字列は文字列に含まれていないことになります。
その場合はNULLポインタを返します。
これが文字列検索の基本的な原理です。

strchrの使い方

strchr()は文字列から特定の文字を検索する関数です。

#include <stdio.h>
#include <string.h>

int main(void) {
    const char *s = "abcdef";  // 検索対象の文字列

    // 'c'という文字を検索
    const char *found = strchr(s, 'c');
    if (found) {
        printf("found[%s]\n", found);
    } else {
        printf("not found\n");
    }
    // found[cdef]

    // 'z'という文字を検索
    found = strchr(s, 'z');
    if (found) {
        printf("found[%s]\n", found);
    } else {
        printf("not found\n");
    }
    // not found

    return 0;
}

strchr()は第1引数に検索対象の文字列、第2引数に検索する文字を取ります。
strchr()検索にヒットすると、見つかった位置の文字列のポインタを返します
↑の場合、abcdefという文字列から文字cを検索しています。
その場合ヒットするのはabcdefcの部分です。
よってstrchr()が返すポインタを出力するとcdefという値になります。

strchr()検索にヒットしなかった場合はNULLポインタを返します
↑の場合、abcdefという文字列から文字zを検索しています。
abcdefの中にzは含まれていないので結果はNULLになります。
strchr()の返り値をチェックして、NULLポインタでなかったら対象の文字列に文字が含まれていて、NULLポインタだったら対象の文字列には文字が含まれていないことになります。

strchr()は第1引数が不正な文字列、つまりNULLポインタなどだった場合はセグフォになる場合があります
つまりstrchr()は第1引数がNULLポインタかどうかチェックしません。

strstrの使い方

strstr()は文字列から特定の文字列を検索する関数です。

#include <stdio.h>
#include <string.h>

int main(void) {
    const char *s = "abcdefghi";

    // "def"という文字列を検索する
    const char *found = strstr(s, "def");
    if (found) {
        printf("found[%s]\n", found);
    } else {
        printf("not found\n");
    }
    // found[defghi]

    // "xyz"という文字列を検索する
    found = strstr(s, "xyz");
    if (found) {
        printf("found[%s]\n", found);
    } else {
        printf("not found\n");
    }
    // not found

    return 0;
}

strstr()は第1引数に検索対象の文字列、第2引数に検索したい文字列を取ります。
strstr()は第1引数の文字列の中に第2引数の文字列が含まれていた場合は、検索にヒットします。
検索にヒットした場合はstrstr()は見つかった所のポインタを返します

↑の場合、abcdefghiという文字列から文字列defを検索しています。
defabcdefghiに含まれています。
そのためstrstr()は返り値としてdの位置のポインタを返します。
そのポインタを出力するとdefghiという風に出力されます。

strstr()検索にヒットしなかった場合はNULLポインタを返します
↑の場合、xyzを検索しています。
xyzabcdefghiという文字列の中に存在しないので、結果はNULLポインタになります。

strstr()の返り値をチェックして、NULLポインタでなかったら特定の文字列が対象の文字列の中に含まれていることがわかります。
NULLポインタであれば対象の文字列に特定の文字列は含まれていません。

strstr()は第1引数または第2引数がNULLポインタ等の不正な文字列だった場合、セグフォになる場合があります
strstr()は第1引数と第2引数をともにNULLポインタかどうかチェックしません。

自力で文字列を検索する

自力で文字列を検索する方法を解説します。
文字列検索のアルゴリズムは色々ありますが、ここでは最も基本的で原始的なアルゴリズムを使います。

文字列の検索は先述の文字列検索の原理と同じことをやります。
つまりナル文字で終端された文字列から、特定の文字列を検索し、ナル文字に届く前にヒットしたらその位置のポインタを返し、ヒットしなかったらNULLポインタを返すわけです。

ここでは↓の検索を取り上げます。

  • 大文字小文字を区別する検索

  • 大文字小文字を区別しない検索

大文字小文字を区別する検索

大文字小文字を区別する文字列検索の関数、find()の実装は↓になります。

#include <stdio.h>
#include <string.h>

/**
 * textからtargetを検索する
 * 検索にヒットしたらポインタを返す
 * 検索にヒットしなかったらNULLポインタを返す
 *
 * @param text   検索対象の文字列
 * @param target 検索したい文字列
 * 
 * @return 見つかった位置のポインタ | NULL
 */
const char *find(const char *text, const char *target) {
    size_t tarlen = strlen(target);

    for (const char *p = text; *p; p += 1) {
        // 現在のポインタ位置からtargetの長さだけ比較する
        if (strncmp(p, target, tarlen) == 0) {
            return p;  // 見つかった
        }
    }

    return NULL;  // 見つからなかった
}

int main(void) {
    const char *s = "abcdefghi";
    const char *found;

    found = find(s, "abc");
    printf("%s\n", found);
    // abcdefghi

    found = find(s, "def");
    printf("%s\n", found);
    // defghi

    found = find(s, "ghi");
    printf("%s\n", found);
    // ghi

    found = find(s, "xyz");
    printf("%p\n", found);
    // (nil)

    return 0;
}

find()は第1引数に検索対象の文字列、第2引数に検索したい文字列を取ります。
検索にヒットしたらポインタを返し、検索にヒットしなかったらNULLポインタを返します。

find()の内部ではまず最初に第2引数の文字列の長さを取得しています。
そしてfor文で第1引数の文字列をポインタで回します。
そして現在のポインタと第2引数の文字列を、第2引数の文字列の長さだけ比較します
これにはstrncmp()を使います。
strncmp()は第3引数の長さだけ第1引数と第2引数の文字列を比較する関数です。
この比較にヒットした場合、目的の文字列が見つかったということになります。
その場合はその時点でのポインタを返します。

for文が終了して検索が最後まで進んだ場合は、検索にヒットしなかったことになります。
その場合はNULLポインタを返します。

このfind()strstr()などと同様に第1引数と第2引数がNULLポインタであるかどうかチェックしません
気になる方はチェックする処理を入れてみてください。

大文字小文字を区別しない検索

find()を改造してfindcase()という関数にします。
この関数は検索の際に大文字小文字を区別しません

#include <stdio.h>
#include <string.h>

/**
 * textからtargetを検索する
 * 大文字と小文字を区別しない
 * 検索にヒットしたらポインタを返す
 * 検索にヒットしなかったらNULLポインタを返す
 *
 * @param text   検索対象の文字列
 * @param target 検索したい文字列
 * 
 * @return 見つかった位置のポインタ | NULL
 */
const char *findcase(const char *text, const char *target) {
    size_t tarlen = strlen(target);

    for (const char *p = text; *p; p += 1) {
        // 現在のポインタ位置からtargetの長さだけ比較する
        if (strncasecmp(p, target, tarlen) == 0) {
            return p;  // 見つかった
        }
    }

    return NULL;  // 見つからなかった
}

int main(void) {
    const char *s = "abcDEFghi";
    const char *found;

    found = findcase(s, "AbC");
    printf("%s\n", found);
    // abcDEFghi

    found = findcase(s, "def");
    printf("%s\n", found);
    // DEFghi

    found = findcase(s, "GHI");
    printf("%s\n", found);
    // ghi

    found = findcase(s, "xyz");
    printf("%p\n", found);
    // (nil)

    return 0;
}

基本的な実装はfind()と同じです。
違うのは文字列の比較にstrncasecmp()を使っている点です。
strncasecmp()は第1引数と第2引数を第3引数の長さだけ比較しますが、その時に大文字と小文字を区別しません。

その他の実装はfind()と同じです。

おわりに

今回はC言語の文字列の検索方法について解説しました。
C言語では文字列も高速で検索することができます。
strchr(), strstr(), あるいは自力で実装する方法を覚えて快適なC言語ライフを送ってください。

検索を制する者は

世界を制する