C言語で文字列を操作する際、特定の文字や文字列がどこに含まれているかを探す場面は非常に多くあります。
標準ライブラリの<string.h>には多くの関数が用意されていますが、中でも頻繁に利用されるのがstrchr関数とstrstr関数です。
これらはどちらも「検索」を目的としていますが、対象となるデータの形式や用途が大きく異なります。
本記事では、これら2つの関数の基本的な使い方から、内部的な動作の違い、さらには実務で役立つ使い分けのポイントまでを詳しく解説します。
ポインタの扱いやメモリ管理といったC言語特有の概念にも触れながら、初心者から中級者までが納得できる内容を目指します。
strchr関数の基本と特徴
strchr関数は、文字列の中から特定の「一文字」を検索するための関数です。
関数名の「chr」は「character(文字)」を意味しており、その名の通り文字単位の処理に特化しています。
strchrのプロトタイプ宣言
strchr関数は以下のように定義されています。
#include <string.h>
char *strchr(const char *s, int c);
第一引数のsは検索対象となる文字列へのポインタです。
第二引数のcは検索したい文字をint型で指定します(内部的にはunsigned charとして扱われます)。
返り値の動作
strchrは、見つかった文字が文字列内で最初に現れた位置のアドレスを返します。
もし指定した文字が見つからなかった場合には、NULLポインタを返します。
この「アドレスを返す」という性質により、見つかった位置から先の文字列をそのまま別の処理に回すことが可能になります。
strchrの使用例
具体的なコードで動作を確認してみましょう。
#include <stdio.h>
#include <string.h>
int main() {
const char *text = "Hello, C Programming World!";
char target = 'P';
char *result;
// 'P'という文字を検索
result = strchr(text, target);
if (result != NULL) {
printf("文字 '%c' が見つかりました。\n", target);
printf("見つかった位置以降の文字列: %s\n", result);
printf("文字列の先頭からのインデックス: %ld\n", result - text);
} else {
printf("文字 '%c' は見つかりませんでした。\n", target);
}
return 0;
}
文字 'P' が見つかりました。
見つかった位置以降の文字列: Programming World!
文字列の先頭からのインデックス: 9
この例では、文字列中の「P」という一文字を探し、その位置からの部分文字列を表示しています。
result - textのようにポインタ演算を行うことで、配列のインデックスとして何番目にあるかを算出することも可能です。
strstr関数の基本と特徴
一方で、strstr関数は文字列の中から別の「文字列(部分文字列)」を検索するために使用されます。
関数名の「str」は「string(文字列)」を意味しています。
strstrのプロトタイプ宣言
#include <string.h>
char *strstr(const char *haystack, const char *needle);
第一引数のhaystack(干し草の山)は検索対象の文字列、第二引数のneedle(針)は検索したい文字列を指します。
「干し草の山から針を探す」という英語の慣用句にちなんだ命名がなされています。
返り値の動作
strstrもstrchrと同様に、一致した部分の先頭アドレスを返します。
見つからなかった場合はNULLを返します。
もしneedleが空文字列(””)である場合、haystackそのものを返す仕様になっています。
strstrの使用例
#include <stdio.h>
#include <string.h>
int main() {
const char *text = "Standard Library Functions";
const char *search_word = "Library";
char *result;
// "Library" という文字列を検索
result = strstr(text, search_word);
if (result != NULL) {
printf("キーワード \"%s\" が見つかりました。\n", search_word);
printf("一致箇所からの残り: %s\n", result);
} else {
printf("キーワード \"%s\" は見つかりませんでした。\n", search_word);
}
return 0;
}
キーワード "Library" が見つかりました。
一致箇所からの残り: Library Functions
単なる文字の検索ではなく、特定の意味を持つ単語やフレーズを検索したい場合には、このstrstrが必要不可欠となります。
strchrとstrstrの決定的な違い
これら2つの関数の使い分けを明確にするために、主な違いを以下の表にまとめました。
| 特徴 | strchr | strstr |
|---|---|---|
| 検索対象 | 一文字 (char) | 文字列 (char *) |
| 主な用途 | 区切り文字(カンマ等)の検出 | 特定キーワードの検索、タグの解析 |
| 引数の型 | int (文字定数) | const char * (文字列リテラル等) |
| 処理速度 | 非常に高速 | 比較対象が多いためstrchrよりは低速 |
| 戻り値 | 一致した最初の文字へのポインタ | 一致した部分文字列の先頭へのポインタ |
1. 検索対象の複雑さ
strchrは、例えば「カンマ区切りのデータからカンマを探す」「パス文字列からスラッシュを探す」といった単純なセパレータ(区切り文字)の検出に非常に適しています。
これに対し、strstrは「HTMLタグの中からhref属性を探す」「ログファイルからERRORという文字列を見つける」といった、意味のあるパターンを探す際に利用します。
2. パフォーマンスの観点
一般的に、strchrは1バイト(環境によってはそれ以上)の比較を繰り返すだけの単純なアルゴリズムで実装されているため、非常に動作が軽快です。
一方のstrstrは、一文字目が一致した後に二文字目、三文字目も一致するかをチェックする必要があるため、計算量は多くなります。
「一文字で済む検索にstrstrを使う」ことは、プログラムの意図を分かりにくくするだけでなく、わずかながら実行効率を低下させる原因にもなります。
実践的な使い分けシーン
具体的な開発現場で、どのようにこれらの関数を使い分けるべきかを見ていきましょう。
カンマ区切り(CSV)のパース処理
CSV形式のデータを一行ずつ読み込み、項目ごとに分割したい場合はstrchrが最適です。
char line[] = "Apple,100,Red";
char *ptr = strchr(line, ',');
if (ptr != NULL) {
*ptr = '\0'; // カンマを終端文字に置き換えて分割
printf("Item: %s\n", line); // "Apple" が出力される
}
このように、区切り文字を検索してヌル文字(\0)に置き換える手法は、C言語の文字列処理における定番のテクニックです。
URLやファイルパスの解析
URLからプロトコル部分(httpなど)を判定したり、ファイル名から拡張子を判別したりする場合、両方を組み合わせることがあります。
- プロトコル(
://)を探す場合はstrstr。 - ファイルパスの最後にあるドット(
.)を探す場合は、strrchr(strchrの逆順版)がよく使われます。
文字列の包含確認
単純に「特定のワードが含まれているかどうか」を知りたいだけであれば、strstrの結果がNULLでないことを確認するだけで十分です。
if (strstr(input_buffer, "QUIT") != NULL) {
// 終了処理へ
}
注意すべきポイントとトラブルシューティング
これらの関数を使用する際には、いくつか注意しなければならない落とし穴があります。
NULLチェックの徹底
もっとも多いバグは、strchrやstrstrが返したポインタに対して、NULLチェックを行わずにアクセスしてしまうことです。
検索対象が見つからなかった場合、返り値はNULLになります。
これに気づかずにポインタを操作(デリファレンス)すると、プログラムはセグメンテーションフォールト(強制終了)を起こします。
const修飾子の扱い
引数にconst char *を渡している場合、返ってくるポインタも実質的にはconstな領域を指しています。
標準ライブラリの定義上、戻り値はchar *(constなし)として返されることが多いですが、読み取り専用の文字列リテラルを書き換えないよう注意が必要です。
終端文字の意識
C言語の文字列は常に\0で終わっている必要があります。
strchrやstrstrは、この終端文字を見つけるまでメモリをスキャンし続けます。
もし終端文字がない不正なバッファを渡してしまうと、バッファオーバーランを引き起こし、深刻なセキュリティ脆弱性の原因となります。
まとめ
C言語におけるstrchrとstrstrは、文字列処理の基本でありながら非常に強力なツールです。
- strchrは「一文字」を高速に探すための関数。
- strstrは「文字列」というパターンを探すための関数。
検索したい対象が「文字」なのか「文字列」なのかを正しく判断し、適切な関数を選択することで、コードの可読性は向上し、バグの混入を防ぐことができます。
特にC言語ではポインタを直接操作するため、戻り値のNULLチェックを忘れないという基本を徹底することが重要です。
これらの関数を使いこなすことができれば、テキスト解析やデータの整形といった、より高度なプログラミングへの第一歩となるでしょう。
日々のコーディングにおいて、どちらが最適かを常に意識しながら実装に取り組んでみてください。
