文字列の検索2
strstr関数
strchr関数は文字列中から「文字」を検索しますが、strstr
関数は文字列中から「文字列」を検索します。
このページの関数を使用するには#include <string.h>
が必要です。
#include <stdio.h>
#include <string.h>
int main()
{
const char* s1 = "I have a pen.";
const char* s2 = "en";
//文字列s1から文字列s2を検索
char* p = strstr(s1, s2);
if (p == NULL)
{
printf("[%s]は[%s]中にありません。", s2, s1);
}
else
{
printf("[%s]は[%s]の%d文字目にあります。", s2, s1, p - s1);
}
getchar();
}
[en]は[I have a pen.]の10文字目にあります。
-
char *strstr(
const char *s1,
const char *s2,
); -
文字列s1内の文字列s2の位置をポインタで返す。
存在しない場合はNULLを返す。
strchr
関数の第二引数が文字から文字列になったものです。
使い方もほとんど同じです。
_mbsstr関数
_mbsstr
関数はstrstr
関数のマルチバイト版対応版です。
_mbschr関数を参照してください。
(ただし、おそらくVisual Studio専用です)
strstr
関数の第二引数は文字列を指定するので、これでもマルチバイト文字の検索ができるようにも見えます。
しかしstrstr
関数は第一引数をシングルバイト文字(1バイト文字)として扱うため、正しく検索できません。
#include <stdio.h>
#include <string.h>
//文字列を16進数で表示
void ShowHex(const char* s)
{
printf("%s\n", s);
while (*s) {
printf("%02X ", *s++);
}
printf("\n");
}
int main()
{
const char* s1 = "初学者";
const char* s2 = "炎";
char* p = strstr(s1, s2);
if (p == NULL)
{
printf("[%s]は[%s]中にありません。\n", s2, s1);
}
else
{
printf("[%s]は[%s]の%dバイト目にあります。\n", s2, s1, p - s1);
}
printf("\n");
ShowHex(s1);
ShowHex(s2);
getchar();
}
[炎]は[初学者]の1バイト目にあります。 初学者 8F 89 8A 77 8E D2 炎 89 8A
このコードでは、「初学者」という文字列の中に「炎」という文字はないのに、strstr
関数で検索すると存在することになっています。
それぞれの文字を数値(16進数)で見ると、「初」は「8F89」、「学」は「8A77」、「者」は「8ED2」で表されています。
「炎」は「898A」で表されています。
(Shift_JISの場合)
正確に検索するには「8F89」「8A77」「8ED2」の三つを検索対象にする必要があります。
しかしstrstr
関数はマルチバイト文字を考慮しないため、「8F89」の次は1バイトずらして「898A」を検索対象にします。
これが「炎」の「898A」と一致してしまうため、正しい検索ができなかったのです。
_mbsstr
関数は正確にマルチバイト文字を判定するため、こういった問題は起こりません。
末尾から文字列を検索したい(strrstr関数?)
strchr
関数が先頭から文字を検索するのに対して、strrchr
関数は末尾から検索します。
strstr
関数は先頭から文字列を検索しますが、末尾から検索する関数はC標準関数にはありません。
(「strrstr関数」みたいなものはない)
末尾から検索する場合は自分で実装する必要があります。
以下にそのサンプルコードを示します。
#include <stdio.h>
#include <string.h>
//文字列から文字列を末尾から検索する
//str: 探される文字列
//search: 探す文字列
//戻り値: 見つかった位置のポインタ
// 見つからなければNULL
char* MyStrrstr(const char* str, const char* search)
{
//どちらかがNULLポインタなら
//検索失敗
if (!str || !search)
return NULL;
const size_t str_len = strlen(str);
const size_t search_len = strlen(search);
//探す文字列より探される文字列のほうが
//短い場合は絶対に見つからない
if (str_len < search_len)
return NULL;
//現在の検索位置を格納する変数
//strの末尾 - searchの文字数の位置にセット
char* p = (char*)str + str_len - search_len;
//pがstrの先頭位置に到達するまでループ
while (p >= str)
{
//pからsearchを検索
if (strncmp(p, search, search_len) == 0)
{
return p;
}
p--;
}
//見つからなかった場合
return NULL;
}
int main()
{
const char* s1 = "What do you want to eat?";
const char* s2 = "at";
char* p = MyStrrstr(s1, s2);
if (p == NULL)
{
printf("[%s]は[%s]中にありません。", s2, s1);
}
else
{
printf("[%s]は[%s]の%d文字目にあります。", s2, s1, p - s1);
}
getchar();
}
[at]は[What do you want to eat?]の21文字目にあります。
メモリから文字列の検索(memstr関数?)
memchr関数はメモリから文字を検索しますが、文字列を検索する関数はC言語標準関数にはありません。
以下は実装のサンプルコードです。
#include <stdio.h>
#include <string.h> //今回の自作関数自体には必要ない
//メモリからバイト列を検索する
//buf: 探すメモリ領域
//bufSize: bufのサイズ
//search: 探すバイト列
//searchSize: searchのサイズ
//reverse: 真(0以外)なら末尾から検索
//戻り値: 見つかった位置のポインタ
// 見つからなければNULL
void* MyMemstr(const void* buf, size_t bufSize, const void* search, size_t searchSize, char reverse)
{
//どちらかがNULLポインタ
//またはsearchSizeが0なら
//検索失敗
if (!buf || !search || searchSize == 0)
return NULL;
//探す文字列より探される文字列のほうが
//短い場合は絶対に見つからない
if (bufSize < searchSize)
return NULL;
//const void*型のままではポインタ演算ができない
//(サイズがわからない)ので
//unsigned char*型に変換
//位置は固定で良いのでconst
const unsigned char* const pBuf = (const unsigned char* const)buf;
const unsigned char* const pSearch = (const unsigned char* const)search;
const size_t maxCount = bufSize - searchSize + 1; //最大検索回数
size_t count = 0; //現在の検索回数
size_t index; //現在の検索位置
//reverseが真ならindexの位置を
//末尾-searchSizeにセット
if (reverse) {
//indexの位置移動用に
//引数reverseを再利用
reverse = -1;
index = bufSize - searchSize;
}
else {
reverse = 1;
index = 0;
}
char mismatch = 0; //検索不一致フラグ
//maxCountに達するまで検索
while (count < maxCount) {
//フラグをクリア
mismatch = 0;
//pBuf+indexの位置からpSearchを検索
for (int n = 0; n < searchSize; n++) {
if (pBuf[index + n] != pSearch[n]) {
//一致しなかった
mismatch = 1;
break;
}
}
//全て一致したら
//その位置を返して処理終了
if (!mismatch)
return (void*)(pBuf + index);
count++;
index += reverse;
}
return NULL;
}
//画面表示用
//引数はMyMemstrと同じ
void Print(const void* buf, size_t bufSize, const void* search, size_t searchSize, char reverse)
{
if (!buf || !search)
{
printf("NULLはダメ\n");
return;
}
unsigned char* pBuf = (unsigned char*)buf;
unsigned char* pSearch = (unsigned char*)search;
printf("探されるバイト列:\n");
for (size_t i = 0; i < bufSize; i++) {
printf("%02X ", pBuf[i]);
}
printf("\n探すバイト列(%s順検索):\n", reverse ? "降" : "昇");
for (size_t i = 0; i < searchSize; i++) {
printf("%02X ", pSearch[i]);
}
char* p = MyMemstr(buf, bufSize, search, searchSize, reverse);
if (p == NULL)
{
printf("\n見つかりませんでした。\n\n");
}
else
{
printf("\n%d番目にあります。\n\n", p - buf);
}
}
int main()
{
const char* s1 = "abcde\0abcde";
const char* s2 = "cd";
Print(s1, 11, s2, strlen(s2), 0);
Print(s1, 11, s2, strlen(s2), 1);
getchar();
}
探されるバイト列: 61 62 63 64 65 00 61 62 63 64 65 探すバイト列(昇順検索): 63 64 2番目にあります。 探されるバイト列: 61 62 63 64 65 00 61 62 63 64 65 探すバイト列(降順検索): 63 64 8番目にあります。
少し長く見えますが、関数内の半分くらいは処理の前準備です。
実際の比較処理はループ文内で、ポインタbuf
の先頭(または末尾)からポインタsearch
の一文字目を探し、見つかったら二文字目同士、三文字目同士と順に比較していきます。
全てが一致したら(不一致と判断されなかったら)そのポインタの位置を返しています。
strstr
関数は途中にNULL文字があるとそれ以上は検索できませんが、この自作関数MyMemstr
ではそれが出来ています。
ついでに、第五引数を真(0以外)にすることで末尾からも検索できるようにしています。