【C++入門】文字列を検索するfind関数(全検索、正規表現)

今回は、find関数を使って文字列を検索する方法を解説していきます。この記事では、

  • find関数とは
  • find関数の使い方
  • find_first_ofの使い方
  • find_last_ofの使い方
  • といった基本的な内容から、部分文字列を全部検索する方法、正規表現で文字列を検索する方法などの応用的な使い方についても解説します。

    目次

    find関数とは

    find関数は、stringクラスの関数で指定した文字列が先頭から検索してどこにあるのかを調べるために使います。指定した文字列が見つからなかった場合にはnposという値を返します。このnposは見つからなかったことを表し、数値的には「-1」となります。

    使い方としては文字列から部分文字列を検索するときなどに使います。ただし、stringクラスを使うためにはstringというライブラリをインクルードする必要があります。

    find関数の使い方

    find関数はfind関数はstringクラスの関数のため、使うにはstring型の変数を宣言する必要があります。

    例として、” samurai, engineer” という文字列から “engineer” という文字列を検索してみます。また、”programmer” という文字列を検索して、見つからなかった場合についてもみていきます。

    #include <iostream>
    #include <string>
    
    int main() {
        std::string str = "samurai,engineer";
    
        int engineerPos = str.find("engineer");
    
        std::cout << "engineerは" << engineerPos << "番目にあります。n";
    
        int programmerPos = str.find("programmer");
    
        if (programmerPos == std::string::npos) {
            std::cout << "programmerは見つかりませんでした。n";
        }else {
            std::cout << "programmerは" << programmerPos << "番目にあります。n";
        }
    
        return 0;
    }

    実行結果:

    engineerは8番目にあります。
    programmerは見つかりませんでした。

    このように、見つかった場合には0から数えた先頭の番号を返して、見つからなかった場合は、nposという特殊な数値を返します。

    rfindで末尾から検索する

    rfind関数を使うことで、末尾から文字列を検索することができます。例として”samurai engineer samurai” という文字列の中から1番最後にsamuraiが出てくる場所を調べることができます。次のプログラムで確認してみましょう。

    #include <iostream>
    #include <string>
    
    int main() {
        std::string str = "samurai engineer samurai";
    
        int samuraiPos = str.rfind("samurai");
    
        if (samuraiPos == std::string::npos) {
            std::cout << "samuraiは見つかりませんでした。n";
        }else {
            std::cout << "samuraiは" << samuraiPos << "番目にあります。n";
        }
    
        return 0;
    }

    実行結果:

    samuraiは17番目にあります。

    find_first_ofを使って先頭から1字を探す

    find_first_of関数を使うと、1字の文字が最初に出てくる場所を調べることができます。例えば、”samurai engineer samurai”という文字列の中から、’a’が最初に出てくる場所を調べることができます。次のプログラムで確認してみましょう。

    #include <iostream>
    #include <string>
    
    int main() {
        std::string str = "samurai engineer samurai";
    
        int aPos = str.find_first_of('a');
    
        if (aPos == std::string::npos) {
            std::cout << 'a' << "は見つかりませんでした。n";
        }else {
            std::cout << 'a' << "は" << aPos << "番目にあります。n";
        }
    
        return 0;
    }

    実行結果:

    aは1番目にあります。

    find_last_ofを使って末尾から1字を探す

    find_last_of関数を使うと、一番最後に見つかった1字の文字を検索することができます。次のプログラムで確認してみましょう。

    #include <iostream>
    #include <string>
    
    int main() {
        std::string str = "samurai engineer samurai";
    
        int last_a_pos = str.find_last_of('a');
    
        if (last_a_pos == std::string::npos) {
            std::cout << 'a' << "は見つかりませんでした。n";
        }else {
            std::cout << 'a' << "は" << last_a_pos << "番目が一番最後に見つかりました。n";
        }
    
        return 0;
    }

    実行結果:

    aは22番目が一番最後に見つかりました。

    部分文字列を全部検索する

    これまで、find関数を使って一番最初、一番最後に見つかった場所を調べることができました。では、文字列が何回出てくるかを調べたいときにはどうすればいいのでしょうか?

    find関数を使うと、範囲を決めて文字列を検索することができます。これを使うことで、文字列がどこに出てくるか全部調べることができます。例として、文字列が見つかった場所をvectorを使って出力してみます。次のプログラムで確認してみましょう。

    #include <iostream>
    #include <string>
    #include <vector>
    
    std::vector<int> find_all(const std::string str, const std::string subStr) {
        std::vector<int> result;
    
        int subStrSize = subStr.size();
        int pos = str.find(subStr);
    
        while (pos != std::string::npos) {
            result.push_back(pos);
            pos = str.find(subStr, pos + subStrSize);
        }
    
        return result;
    }
    
    int main() {
        std::string str = "samurai engineer samurai",
                    subStr = "samurai";
    
        std::vector<int> findVec = find_all(str, subStr);
    
        std::cout << subStr << "はn";
    
        for (const auto &pos:findVec) {
            std::cout << pos << "番目n";
        }
    
        std::cout << "にあります。n";
    
        return 0;
    }

    実行結果:

    samuraiは
    0番目
    17番目
    にあります。

    vectorとは、配列の長さがわからない時などに使われる動的配列のことです。vectorの詳しい使い方はこちらの記事で解説しているので、ぜひ確認してください。

    regex_searchで正規表現を使って文字列を検索する

    C++11以降では、regex_search関数を使って文字列を正規表現で検索することができます。

    正規表現とは

    正規表現を使うと、文字列をまとめて検索ができ、文字列の検索がしやすくなります。例えば、数字3文字が連続する場所を調べたいときには、

    "[0-9]{3}";

    のように書きます。正規表現の詳しい使い方はこちらの記事で解説しているので、ぜひ確認してください。

    正規表現の使い方

    正規表現を使うには、regexというライブラリをインクルードする必要があります。正規表現を宣言するには、

    std::regex 変数名 (正規表現);

    となります。

    ただし、正規表現で”d”といった””マークなどを使いたいときは、エスケープする必要があるので、”d”というように2回連続で書く必要があります。

    正規表現文字列を検索する

    regex_search関数を使うことで、文字列を正規表現で調べることができます。regex_searchは、以下のように引数をとります。

    std::regex_search(検索される文字列, 結果, 正規表現);

    そして、見つかった場合はtrue、見つからなかった場合はfalseを返します。この結果というのは、 std::smatchという型の変数です。これを使うことで、マッチした文字列の数、最初に出てきた文字列、マッチした文字列全体を得ることができます。

    例として、文字列の中から数字だけを取り出す方法を次のプログラムで確認してみましょう。

    #include <iostream>
    #include <string>
    #include <regex>
    
    int main() {
        std::string str = "私は18歳です。";
        std::regex re("d+");
        std::smatch m;
    
        if (std::regex_search(str, m, re)) {
            std::cout << m.str() << "が見つかりました。n";
        }
    
        return 0;
    }

    実行結果:

    18が見つかりました。

    大文字小文字を区別せず検索する

    正規表現を使うと、大文字小文字を区別せず調べることができます。そのためには、正規表現を宣言するときに、

    std::regex(正規表現, std::regex_constants::icase);

    のように付け足します。なお、文字列は書き変えていないため、検索結果の文字列はそのままの状態で表示されます。例として大文字小文字を区別せず “samurai” という文字列を取得する方法を次のプログラムで確認してみましょう。

    #include <iostream>
    #include <string>
    #include <regex>
    
    int main() {
        std::string str = "I am a SAMURAI from Japan.";
        std::regex re("samurai", std::regex_constants::icase);
        std::smatch m;
    
        while (std::regex_search(str, m, re)) {
            std::cout << m.str() << "が見つかりました。n";
            str = m.suffix();
        }
    
        return 0;
    }

    実行結果:

    SAMURAIが見つかりました。

    正規表現で全て取得

    検索結果に合うものを全て得るには、suffix関数を使って検索結果の後ろの文字列を取得します。そしてその後ろの文字列からまた検索していき、全て取得することができます。例として、文字列から数値を全て取得する方法を次のプログラムで確認してみましょう。

    #include <iostream>
    #include <string>
    #include <regex>
    
    int main() {
        std::string str = "2018/4/20 17:00";
        std::regex re("d+");
        std::smatch m;
    
        while (std::regex_search(str, m, re)) {
            std::cout << m.str() << "が見つかりました。n";
            str = m.suffix();
        }
    
        return 0;
    }

    実行結果:

    2018が見つかりました。
    4が見つかりました。
    20が見つかりました。
    17が見つかりました。
    00が見つかりました。

    まとめ

    いかがだったでしょうか?今回は文字列からある文字列を検索する方法を解説しました。文字列から

    • 特定の文字列を探したい
    • 正規表現を使って数値のみを検索したい
    • 大文字小文字を区別せず調べたい

    などの場合に使ってみてください。もし、文字列から検索する方法を忘れてしまったらこの記事を確認してください。

    この記事を書いた人

    ご閲覧いただきありがとうございます。森田一世と申します。プログラマーとしてRaspberry piを使ったり、記事を作成しています。

    目次