閉じる

【C言語】文字列を検索する(strchr, strstr) 基本と使い方まとめ

文字列の中から特定の文字や語句を探す処理は、入力解析やログ抽出など多くの場面で登場します。

本記事ではC言語標準のstrchrstrstrを中心に、基本の使い方から返り値の扱い、見つからない場合の分岐、複数回見つける方法や注意点までを丁寧に解説します。

返り値のポインタとNULLの扱いが最大の要点です。

文字列検索の基本

必要なヘッダ

これらの関数はstring.hに宣言されています。

サンプルの出力にはstdio.h、ポインタ差を出力する場合はstddef.hが必要です。

C言語
#include <string.h>  // strchr, strstr
#include <stdio.h>   // printf (サンプル出力用)
#include <stddef.h>  // ptrdiff_t (ポインタ差の型)

必ずヌル終端された文字列を渡すことが前提です。

配列の一部だけを検索したい場合はmemchrなど別系統の関数を検討します。

strchrとstrstrの違い

1文字を探すのがstrchr部分文字列(複数文字)を探すのがstrstrです。

どちらも見つかった最初の位置へのポインタを返すか、見つからなければNULLを返します。

項目strchrstrstr
探す対象1文字(cとして渡す)文字列(needle)
検索対象ヌル終端文字列(s)ヌル終端文字列(haystack)
返り値見つかった文字へのポインタ or NULL見つかった部分文字列の先頭へのポインタ or NULL
特殊扱いcが'\0'なら終端位置を返すneedleが空文字ならhaystack先頭を返す
大文字小文字区別する区別する

両者とも線形走査のO(n)アルゴリズムです。

高速化が必要ならアルゴリズムの工夫(例えばBoyer–Moore系)やライブラリの活用を検討します。

返り値とNULLの意味

返り値は「見つかった位置」へのポインタです。

見つからないとNULLが返るため、必ずNULLチェックを行ってから参照やインデックス計算をしてください。

C言語
const char *s = "example";
const char *p = strchr(s, 'm');
if (p != NULL) {
    // pはsの中の'm'を指す
} else {
    // 見つからなかった
}

ヌル終端の前提

これらの関数はヌル文字'\0'に遭遇するまで走査します。

終端がないバッファや、途中に意図しない'\0'があるデータでは期待通りに動きません。

未終端のメモリ領域を渡すのは未定義動作です。

strchrの使い方

関数の書式と引数

C言語
// C標準
char *strchr(const char *s, int c);
  • s: 検索対象のヌル終端文字列
  • c: 探す文字(整数型)。unsigned charとして表現可能な値'\0'を渡します

実装によってはcharが符号付きのため、拡張文字(0x80以上)を扱う場合は(unsigned char)にキャストしてからintに昇格させると安全です。

基本のサンプル

C言語
#include <string.h>
#include <stdio.h>
#include <stddef.h>

int main(void) {
    char s[] = "Hello, world";
    char *p = strchr(s, 'o');        // 最初の'o'を探す
    if (p != NULL) {
        ptrdiff_t idx = p - s;       // インデックス(0始まり)
        printf("found '%c' at index %td, substring: \"%s\"\n", *p, idx, p);
    }
    return 0;
}
実行結果
found 'o' at index 4, substring: "o, world"

最初に見つかった位置を得る

strchr先頭から最初に一致した位置のみを返します。

同じ文字が複数回出現しても、返り値は常に最初の一致です。

C言語
#include <string.h>
#include <stdio.h>
#include <stddef.h>

int main(void) {
    const char *s = "bookkeeper";
    const char *p = strchr(s, 'e');  // 'e'は何度か出るが最初の'e'のみ
    if (p) {
        printf("first 'e' index: %td\n", p - s);
    }
    return 0;
}
実行結果
first 'e' index: 5

見つからない場合

見つからないとNULLが返ります。

NULLのまま参照しないでください。

C言語
#include <string.h>
#include <stdio.h>

int main(void) {
    const char *s = "abcdef";
    const char *p = strchr(s, 'x');
    if (p == NULL) {
        printf("not found\n");
    }
    return 0;
}
実行結果
not found

インデックスの求め方

ポインタ差p - sで0始まりの位置が得られます。

型はptrdiff_tです。

C言語
#include <string.h>
#include <stdio.h>
#include <stddef.h>

int main(void) {
    const char *s = "ABCDE";
    const char *p = strchr(s, 'D');
    if (p) {
        ptrdiff_t idx = p - s;
        printf("index: %td\n", idx);
    }
    return 0;
}
実行結果
index: 3

特殊ケース

終端文字'\0'を探す

文字列の末尾(終端)を指すポインタが返ります。

改行など制御文字

'\n''\t'も通常の文字定数として検索できます。

拡張文字

ロケールや実装でcharが符号付きの場合、strchr(s, (unsigned char)ch)の形が安全です。

未終端データ

バイナリデータや途中に'\0'を含むデータではstrchrは途中で止まります。長さが分かっている場合はmemchrの検討をおすすめします。

C言語
#include <string.h>
#include <stdio.h>

int main(void) {
    const char *s = "abc";
    const char *p = strchr(s, '
#include <string.h>
#include <stdio.h>
int main(void) {
const char *s = "abc";
const char *p = strchr(s, '\0'); // 終端を探す
if (p) {
printf("length via strchr: %ld\n", (long)(p - s)); // strlenと同じ
}
return 0;
}
'); // 終端を探す if (p) { printf("length via strchr: %ld\n", (long)(p - s)); // strlenと同じ } return 0; }
実行結果
length via strchr: 3

大文字小文字は区別

strchrケースセンシティブです。

C言語
#include <string.h>
#include <stdio.h>

int main(void) {
    const char *s = "Abc";
    printf("%s\n", strchr(s, 'a') ? "found" : "not found"); // 'A' != 'a'
    printf("%s\n", strchr(s, 'A') ? "found" : "not found");
    return 0;
}
実行結果
not found
found

大文字小文字を無視したい場合は、POSIXのstrcasestr(非標準)を使うか、文字を都度tolower/toupperで正規化して比較する方法があります(詳細は別記事で扱います)。

strstrの使い方

関数の書式と引数

C言語
// C標準
char *strstr(const char *haystack, const char *needle);
  • haystack: 検索対象の文字列
  • needle: 探す部分文字列(空文字も可)

基本のサンプル

C言語
#include <string.h>
#include <stdio.h>
#include <stddef.h>

int main(void) {
    const char *s = "banana bread";
    const char *p = strstr(s, "ana"); // 最初の"ana"
    if (p) {
        printf("found at index %td: \"%s\"\n", p - s, p);
    }
    return 0;
}
実行結果
found at index 1: "anana bread"

部分文字列の最初の位置を得る

返り値ポインタ差p - sで先頭のインデックスが求まります。

以降を表示すると、見つかった位置から末尾までが確認できます。

needleが空文字のとき

needle""なら常にhaystack先頭のポインタが返ります。

C言語
#include <string.h>
#include <stdio.h>
#include <stddef.h>

int main(void) {
    const char *s = "abc";
    const char *p = strstr(s, "");  // 空文字
    printf("index: %td\n", p - s);  // 常に0
    return 0;
}
実行結果
index: 0

見つからない場合

見つからないとNULLが返ります。

C言語
#include <string.h>
#include <stdio.h>

int main(void) {
    const char *s = "pineapple";
    printf("%s\n", strstr(s, "pear") ? "found" : "not found");
    return 0;
}
実行結果
not found

大文字小文字は区別

strstrケースセンシティブです。

C言語
#include <string.h>
#include <stdio.h>

int main(void) {
    const char *s = "Hello World";
    printf("%s\n", strstr(s, "world") ? "found" : "not found");
    printf("%s\n", strstr(s, "World") ? "found" : "not found");
    return 0;
}
実行結果
not found
found

ケース無視検索はPOSIXのstrcasestrや、自前で小文字化してからstrstrを使うなどの方法があります(本記事では詳細割愛)。

複数回見つける方法

次の位置から繰り返しstrstrを呼びます。

重なりを許すか否かで進め方が異なります。

C言語
#include <string.h>
#include <stdio.h>
#include <stddef.h>

int main(void) {
    const char *s = "bananana";
    const char *needle = "ana";
    size_t nlen = strlen(needle);

    // 1) オーバーラップを許す検索
    printf("overlap allowed:\n");
    const char *p = s;
    while ((p = strstr(p, needle)) != NULL) {
        printf("  index %td\n", p - s);
        p = p + 1; // 1文字進めて次も探索(重なり許容)
    }

    // 2) オーバーラップを許さない検索
    printf("no overlap:\n");
    p = s;
    while ((p = strstr(p, needle)) != NULL) {
        printf("  index %td\n", p - s);
        p = p + nlen; // 見つかった語の分だけスキップ
    }
    return 0;
}
実行結果
overlap allowed:
  index 1
  index 3
  index 5
no overlap:
  index 1
  index 5

よくある用途と注意点

文字の有無を判定する

1文字の出現チェックはstrchrで簡潔に記述できます。

条件式にそのまま使うと可読性が高まります。

C言語
#include <string.h>
#include <stdio.h>

int main(void) {
    const char *s = "path/to/file.txt";
    if (strchr(s, '/')) {
        printf("has directory separator\n");
    }
    return 0;
}
実行結果
has directory separator

キーワードを含むか判定する

strstrでキーワードの包含判定ができます。

先頭一致か部分一致かを要件に応じて使い分けます。

C言語
#include <string.h>
#include <stdio.h>

int main(void) {
    const char *line = "GET /index.html HTTP/1.1";
    if (strstr(line, "HTTP/1.1")) {
        printf("HTTP/1.1 request\n");
    }
    return 0;
}
実行結果
HTTP/1.1 request

次の位置から検索を続ける

返り値ポインタを基準に1文字進めて再検索するのが基本です。

非重複にしたい場合はneedleの長さだけスキップします。

C言語
#include <string.h>
#include <stdio.h>

int main(void) {
    const char *s = "Hello, world";
    const char *p = s;
    while ((p = strchr(p, 'l')) != NULL) {
        printf("found at %ld\n", (long)(p - s));
        p = p + 1; // 次の位置から継続
    }
    return 0;
}
実行結果
found at 2
found at 3
found at 10

ポインタからインデックスへ変換する

インデックスはptrdiff_t idx = p - s;で得ます。

配列インデックスに使うなら適宜size_tにキャストします(負にならないことが前提)。

C言語
#include <string.h>
#include <stdio.h>
#include <stddef.h>

int main(void) {
    const char *s = "indexing";
    const char *p = strchr(s, 'x');
    if (p) {
        ptrdiff_t idx = p - s;   // 標準的なやり方
        printf("%td\n", idx);    // C99以降の%td指定
    }
    return 0;
}
実行結果
4

NULLのまま参照しない

NULLチェックを忘れて*pp[0]に触れると未定義動作です。

見つからなかった分岐を必ず記述します。

C言語
#include <string.h>
#include <stdio.h>

int main(void) {
    const char *s = "data";
    const char *p = strchr(s, 'z');
    if (p == NULL) {
        printf("safe: not found\n");
        // 危険: printf("%c\n", *p); // これはダメ
    }
    return 0;
}
実行結果
safe: not found

マルチバイト文字への注意

これらの関数は「バイト列として」検索します。UTF-8やShift_JISのマルチバイト文字を1文字としては扱いません

strchrは1バイトの文字しか検索できません。例えばUTF-8の「あ」(3バイト)はstrchrでは探せません。

strstrはマルチバイトの「並び」を検索できるため、UTF-8の「あ」を含むバイト列は見つけられますが、コードポイントの境界を理解しているわけではありません。

より厳密に国際化対応したい場合はwchar_t系(wcschr, wcsstr)や、ICUなどのライブラリを検討してください(用途により選択)。

まとめ

文字(1バイト)を探すならstrchr、語句(部分文字列)を探すならstrstrが基本です。

返り値は「見つかった位置へのポインタ」またはNULLであり、必ずNULLチェックを行い、p - sでインデックスを得られる点を押さえておくと実装が安定します。

加えて、ヌル終端前提ケースセンシティブマルチバイト文字はバイト列として扱われるという性質に注意してください。

これらの基本を正しく使い分けることで、文字列検索処理を安全かつ明瞭に記述できます。

この記事を書いた人
エーテリア編集部
エーテリア編集部

プログラミングの基礎をしっかり学びたい方向けに、C言語の基本文法から解説しています。ポインタやメモリ管理も少しずつ理解できるよう工夫しています。

クラウドSSLサイトシールは安心の証です。

URLをコピーしました!