C++ 文字列の検索を行う方法【std::string|find/find_first_of/search】

C++の文字列型(std::string)に対する文字列検索を行う場合には、一般的なfindメンバ関数を用いる方法のほかに、find_first_ofメンバ関数やstd::search関数を用いる方法などがあります。

目次

find/rfind

std::string型のメンバ関数findを用いることで、文字列の出現位置を判定することができます。findメンバ関数は検索対象の文字列が最初に出現する位置を返します。逆にrfindは最後に出現した位置を返します。

std::string s = "a-b-c";
auto pos = s.find("-"); // pos == 1 (先頭から検索)
pos = s.rfind("-");     // pos == 3 (末尾から検索)

s.find('b');              // 2 (文字型による検索)
s.find(std::string("c")); // 4 (std::string型による検索)
s.find("");               // 0
s.find("9");              // std::string::npos (見つからなかった場合)

第一引数には検索したい文字列または文字型の値を指定します。戻り値は検索文字列の出現位置のインデックスをstd::string::size_type型の値として返します。検索した値が見つからなかった場合には、戻り値としてメンバ定数std::string::nposが返されます。

オプションとして、第二引数に検索開始位置を指定することもできます。第一引数に指定した検索文字列がC言語スタイルの文字列の場合には、第三引数に検索文字列の長さを指定することもできます。

std::string s = "a-b-c";
s.find("-", 2);  // 3
s.rfind("-", 2); // 1 (※末尾からのオフセットではない)

s.find("-c-", 0, 1); // 1 (部分文字列 "-"  による検索)
s.find("-c-", 0, 2); // 3 (部分文字列 "-c" による検索)

char a[] = {'-', 'c'};   /* 非NULL終端文字列への応用 */
s.find(a, 0, sizeof(a)); // 3

find_first_of/find_last_of

find_first_ofメンバ関数は、最初に出現する文字または文字集合を検索する機能です。

std::string s = "a-b=c";

/* 文字の検索 */
s.find_first_of('-');  // 1
s.find_last_of('=');   // 3

/* 文字集合の検索 */
s.find_first_of("=-"); // 1 ('=', '-'のいずれかが最初に出現した位置)
s.find_last_of("=-");  // 3 ('=', '-'のいずれかが最後に出現した位置)

第二引数には検索開始位置、第三引数は第一引数の文字列の長さを指定することができます。基本的な使い方は# find/rfindメンバ関数と同じです。

std::string s = "a-b";
s.find_first_of("-=", 1, 1); // 1
s.find_first_of("=-", 1, 1); // std::string::npos
s.find_first_of("=-", 1, 2); // 1

find_first_of と find の違い

文字列型の値を受け取るfind_first_of関数は文字集合による検索を行います。指定された文字列内のいずれかの文字が最初に出現する位置を返します。これはC言語標準ライブラリのstrpbrk関数に相当する機能となります。

文字型の値を受け取るfind_first_of関数とfind関数は同じ動作をします(一般的には速度的な違いもありません)。

find_first_not_of/find_last_not_of

文字/文字集合に一致しない位置を検索するためのfind_first_not_of/find_last_not_ofメンバ関数も存在します。

std::string s = "$99";

/* find_first_not_of: 最初に一致しなかった位置の検索 */
s.find_first_not_of('$'); // 1
s.find_first_not_of('9'); // 0
s.find_first_not_of('a'); // 0

/* find_last_not_of:  最後に一致しなかった位置の検索 */
s.find_last_not_of('$'); // 2
s.find_last_not_of('9'); // 0
s.find_last_not_of('a'); // 2

// 見つからなかった場合は`std::string::npos`を返す
std::string("$").find_first_not_of('$'); // npos
std::string("$").find_last_not_of('$');  // npos

if (s.find_first_not_of("$") != std::string::npos)
  s.substr(s.find_first_not_of('$'));    // "99"

std::string("-c-d").find_first_not_of('-'); // 1
std::string("a-b-").find_last_not_of('-');  // 2

アルゴリズム関数による検索

algorithmヘッダーのテンプレート関数を用いることで、イテレータの活用や検索範囲の指定による、より柔軟な検索が行えます。

次に紹介する# std::find/std::find_if/std::find_if_not/std::find_first_of 関数は文字型の値等の要素に対する検索に対応しています。# std::search/std::find_end 関数は文字列等のサブシーケンスの検索に対応しています。

これらのアルゴリズム関数はstd::string型以外にも配列型やstd::vector等のコンテナ、C言語スタイルの文字列に対する検索も可能となっています。

std::find/std::find_if/std::find_if_not/std::find_first_of 関数

文字等の単一の要素を検索する場合には、algorithmヘッダーのfind系アルゴリズム関数を用いることができます。基本的な動作は先程紹介したstd::string型のメンバ関数のものと同等ですが、検索範囲の指定にイテレータを用いる点が異なります。また戻り値も同様にイテレータとなります。

// #include <algorithm> // std::find, std::find_if, std::find_if_not, std::find_first_of
std::string s = "a-b-b";

// find: 要素の検索
std::find(s.begin(), s.end(), '-') == s.begin() + 1; // true (以下全て真となる条件式)
std::find(s.begin(), s.end(), '=') == s.end(); // 見つからない場合

// find_if: 関数オブジェクトによる要素の判定
std::find_if(s.begin(), s.end(), isalpha) == s.begin();
std::find_if(s.begin(), s.end(), [](char c) {
  return c >= 'b' && c <= 'z';
}) == s.begin() + 2;

// find_if_not: 要素と一致しない箇所の検索
std::find_if_not(s.begin(), s.end(), isalpha) == s.begin() + 1;

// find_first_of: 文字集合と一致する箇所の検索
std::string set = "=-"; // 文字集合(いずれかの文字を検索)
std::find_first_of(s.begin(), s.end(), set.begin(), set.end()) == s.begin() + 1;

第一引数から第二引数に検索先のシーケンスの範囲を指定します。第三引数には検索したい文字などの条件を指定します。文字が見つからなかった場合にはシーケンス中の終端要素(第二引数に指定された値)が返されます。

以下のサンプルはC言語スタイルの文字列に対する検索を行う場合の例です。C言語標準ライブラリのstrchr関数の活用例に相当する処理となります。

const char* s = "a-b";
size_t n = std::char_traits<char>::length(s);

const char* ptr = std::find(s, s + n, '-'); // `strchr(s, '-')`に相当
puts(ptr);  // "-b"
ptr - s;    // 1 (出現位置の計算)

/*       */ ptr = std::find(s, s + n, '='); // 見つからないケース
*ptr == '\0'; // 終端文字の判定
ptr == s + std::char_traits<char>::length(s); // ポインタ比較による終端位置の判定

std::search/std::find_end 関数

文字列を検索する場合にはalgorithmヘッダーのアルゴリズム関数std::searchstd::find_endを用います。両関数の戻り値は出現位置のインデックスではなく、一致した文字列の位置を指すイテレータとなります。search関数は検索文字列が最初に一致した位置、find_endは最後に一致した位置を返します。

// #include <algorithm> // std::search, std::find_end
// #include <iterator>  // std::distance
std::string s = "a-b-b";
std::string t = "-b";

/* 最初に見つかった位置 */
auto r = std::search(s.begin(), s.end(), t.begin(), t.end());
if (r != s.end()) {
  std::distance(s.begin(), r); // 1      (出現位置の計算)
  r == (s.begin() + 1);        // true   (出現箇所の比較)
  std::string(s.begin(), r);   // "a"    (前方文字列の抽出)
  std::string(r, s.end());     // "-b-b" (部分文字列の抽出)
}

/* 最後に見つかった位置 */
auto w = std::find_end(s.begin(), s.end(), t.begin(), t.end());
if (w != s.end()) {
  std::distance(s.begin(), w); // 3      (出現位置の計算)
  w == (s.begin() + 3);        // true   (出現箇所の比較)
  std::string(w, s.end());     // "-b"   (部分文字列の抽出)
}

第一引数から第二引数には検索先のシーケンスの範囲を指定し、第三引数から第四引数には検索対象のサブシーケンスの範囲を指定します。

サブシーケンスが見つからなかった場合にはシーケンスの終端を表すイテレータが返されます。ただし、サブシーケンスが空文字の場合にはsearch関数はシーケンス中の先頭の位置を指すイテレータを返す点に注意が必要です。

std::string s = "s";

// 見つからなかった場合には終端のイテレータを返す(第二引数と同じ値)
std::string t = "t";
s.end() == std::search(s.begin(), s.end(), t.begin(), t.end());
s.end() == std::find_end(s.begin(), s.end(), t.begin(), t.end());

// std::search  : 空文字の場合は最初のイテレータを返す
// std::find_end: 空文字の場合は最後のイテレータを返す
std::string e = "";
s.begin() == std::search(s.begin(), s.end(), e.begin(), e.end());
s.end() == std::find_end(s.begin(), s.end(), e.begin(), e.end());
広告