HOWTO · C
C 言語で文字列にサブストリングが含まれているかどうかをチェックする
この記事では、C 言語で文字列に部分文字列が含まれているかどうかをチェックする方法を示します。
このページの内容
ヌル終端されたC文字列に、大小文字を区別する完全一致の部分文字列が含まれるか調べるには、<string.h> の strstr を呼び出します。一致した場合は最初の位置を指すポインターを返し、見つからない場合は NULL を返します。以下の例は実行済みで、いずれも Substring found. と出力しました。この出力ラベルはコード例をそのまま保つため、英語のままです。
strstr で部分文字列を検索する
検索対象の文字列を第1引数に、探す文字列を第2引数に渡します。戻り値のポインターをそのまま判定してください。ポインターをアドレスとして出力すると、プロセスごとに値が変わるため避けましょう。strstr は標準Cヘッダー <string.h> で宣言されており、英字の大文字・小文字も含め、文字を完全に比較します。
次のプログラムは文中に welcome があるか調べ、見つかったかどうかを一定のメッセージで表示します。
#include <stdio.h>
#include <string.h>
int main(void) {
const char *text = "Hello, welcome to C.";
const char *needle = "welcome";
if (strstr(text, needle) != NULL) {
puts("Substring found.");
} else {
puts("Substring not found.");
}
return 0;
}
Substring found.
戻り値のポインターは、text 内で最初に一致した文字を指します。その位置から内容を調べたり、検索を続けたりできます。ポインターが NULL でなければ、一致する部分文字列があります。検索文字列が "" の場合、strstr は text の先頭を返します。空の検索文字列を一致として扱いたくない場合は、この標準の動作を考慮してください。
ループで部分文字列を検索する
手動検索を使うと、比較の流れを明示したり、アプリケーション固有の処理を加えたりできます。次の関数は、どちらも NULL ではないヌル終端文字列を2つ受け取り、検索文字列が含まれていれば 1、含まれていなければ 0 を返します。長さを引き算する前に大小を確認するため、検索文字列が入力より長い場合に符号なし整数がアンダーフローするのを防げます。空の検索文字列は strstr と同様に一致として扱います。
#include <stdio.h>
#include <string.h>
int contains_substring(const char *text, const char *needle) {
size_t text_len = strlen(text);
size_t needle_len = strlen(needle);
if (needle_len == 0) {
return 1;
}
if (needle_len > text_len) {
return 0;
}
for (size_t i = 0; i <= text_len - needle_len; ++i) {
size_t j = 0;
while (j < needle_len && text[i + j] == needle[j]) {
++j;
}
if (j == needle_len) {
return 1;
}
}
return 0;
}
int main(void) {
const char *text = "Hello, welcome to C.";
const char *needle = "welcome";
puts(contains_substring(text, needle)
? "Substring found."
: "Substring not found.");
return 0;
}
Substring found.
外側のループでは、検索文字列全体が収まる各開始位置を試します。内側のループは、対応する文字が一致している間だけ進みます。検索文字列の長さまで到達すれば、完全に一致したことになります。このシンプルな方法は学習や独自の処理の追加に役立ちます。通常の完全一致検索では、自前の検索コードを保守する理由がなければ標準ライブラリ関数を使いましょう。
大文字・小文字を区別せずに部分文字列を検索する
C標準ライブラリには、大文字・小文字を区別しない部分文字列検索関数はありません。GNUシステムでは、GNU拡張として提供される strcasestr を使うと、大文字・小文字を区別せずに検索できます。この機能テストマクロを使うシステムでは、宣言を有効にするため、ヘッダーをインクルードする前に _GNU_SOURCE を定義してください。これは標準Cの移植可能な関数ではないため、利用する前に対象プラットフォームで使えることを確認しましょう。
次の完結した例はASCIIテキストを使い、GNU固有の呼び出し方を示します。出力は Substring found. です。このラベルはコード例をそのまま保つため英語のままです。
#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
int main(void) {
const char *text = "Hello, Welcome to C.";
const char *needle = "welcome";
if (strcasestr(text, needle) != NULL) {
puts("Substring found.");
} else {
puts("Substring not found.");
}
return 0;
}
Substring found.
移植性が必要なコードでは、入力文字列を暗黙に変更せず、文字の扱い方を明示して大文字・小文字を区別しない比較を実装します。バイト単位のテキストなら tolower が役立ちますが、引数には EOF、または unsigned char で表せる値を渡す必要があります。また、その動作は有効なCロケールに従います。この方法だけではUnicodeのケースフォールディングはできません。処理するデータに合ったライブラリとテキストの扱い方を選んでください。
エッジケースと方法の選び方
strstr と上記のループはいずれもヌル終端文字列を検索し、文字列の並びを完全一致で比較します。検索文字列が入力より長ければ一致しません。空の検索文字列の場合、strstr は先頭で一致したと報告し、上記のループも同じ扱いです。アプリケーションで別のルールが必要なら、明示的に確認してください。標準の文字列関数と同じく、このループも有効な文字列を指す NULL 以外のポインターを必要とします。
strstr が返すのは最初の出現位置です。後続の出現箇所を探すには、検索文字列が空でないことを確認したうえで、前回の一致位置に検索文字列の長さを加えた位置から検索を続けます。重なり合う一致も対象にする場合は、前回の一致位置から1文字だけ進めてください。どちらを選ぶかは、一致箇所が文字を共有してよいかどうかで決まります。
通常の完全一致かつ大文字・小文字を区別する検索には、簡潔で標準Cの一部でもある strstr を使います。比較方法を学ぶため、または独自に変更するために手動ループを使う場合は、入力の前提条件を記載しましょう。strcasestr は、GNU拡張が利用でき、バイト単位およびロケールに基づく動作がテキストに適している場合に限って使います。strpbrk は完全一致の部分文字列検索の代わりにはなりません。入力に文字集合のいずれかの文字が含まれるかを調べる関数であり、別の問いに答えるものです。