C++ Academy · レッスン

パフォーマンスに関する注意点

パターンを適切にコンパイルします

レッスン 4/413 ステップ

「パフォーマンスに関する注意点」はCoddyKit上の無料C++ Academyレッスンです。 これはレッスン4/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはC++ Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 C++ Academyコースには全4レッスンが含まれています。

コンパイルにはコストがかかる

文字列からstd::regexを構築するとパターンがコンパイルされるため、コストがかかります。最も効果的なのは、1回だけコンパイルして再利用することです。

  • ループ内でregexを構築しない
  • 起動時または初回使用時にコンパイルする
#include <iostream>
#include <regex>

int main() {
    std::regex p("\\d+");
    std::string inputs[] = {"12", "ab", "99"};
    for (const auto& s : inputs) {
        std::cout << s << ": " << std::regex_match(s, p) << '\n';
    }
    return 0;
}

コンパイル済みパターンの再利用

ループの外でパターンを1回構築し、すべての入力に対して再利用します。反復ごとに再構築すると時間を無駄にします。

#include <iostream>
#include <regex>
#include <vector>

int main() {
    static const std::regex p("[a-z]+");
    std::vector<std::string> words = {"hello", "WORLD", "cpp"};
    int matches = 0;
    for (const auto& w : words) {
        if (std::regex_match(w, p)) ++matches;
    }
    std::cout << "Matched: " << matches << '\n';
    return 0;
}

定数にはstatic constを使う

変化しないパターンの場合は、関数内でstatic constとして宣言すると、すべての呼び出しを通じて1回だけコンパイルされます。

#include <iostream>
#include <regex>

bool isEmail(const std::string& s) {
    static const std::regex p("\\w+@\\w+\\.\\w+");
    return std::regex_match(s, p);
}

int main() {
    std::cout << std::boolalpha;
    std::cout << isEmail("a@b.com") << '\n';
    std::cout << isEmail("nope") << '\n';
    return 0;
}

適切な文法を選ぶ

regexの種類(文法)は速度と機能に影響します。std::regex::ECMAScriptがデフォルトで最も機能が豊富です。std::regex::basicやextendedはより単純で、場合によっては高速です。

#include <iostream>
#include <regex>

int main() {
    std::regex p("a+", std::regex::extended);
    std::cout << std::boolalpha << std::regex_match("aaa", p) << '\n';
    return 0;
}

optimizeフラグ

std::regex::optimizeを追加すると、マッチングを高速化する代わりに、エンジンがパターンの構築により多くの時間をかけるよう指示できます。何度もマッチングする場合に効果があります。

#include <iostream>
#include <regex>

int main() {
    std::regex p("\\d{3}-\\d{4}", std::regex::optimize);
    std::cout << std::boolalpha << std::regex_match("123-4567", p) << '\n';
    return 0;
}

壊滅的なバックトラッキングを避ける

(a+)+のような入れ子になった量指定子は、特定の入力に対して指数関数的に処理時間が増大することがあります。より単純で曖昧さのないパターンを使います。

#include <iostream>
#include <regex>

int main() {
    std::regex good("a+b");
    std::cout << std::boolalpha << std::regex_match("aaaab", good) << '\n';
    return 0;
}

可能な場合はアンカーを使う

^と$でアンカーを指定すると、エンジンはすべての開始位置を試すのではなく、すぐに失敗を判定できます。

#include <iostream>
#include <regex>

int main() {
    std::regex p("^[a-z]{3}$");
    std::cout << std::boolalpha;
    std::cout << std::regex_search("abc", p) << '\n';
    std::cout << std::regex_search("abcd", p) << '\n';
    return 0;
}

可能なら単純なツールを優先する

固定部分文字列の確認だけが必要なら、std::string::findのほうがregexよりはるかに高速です。本当にパターンマッチングが必要な場合にだけregexを使います。

#include <iostream>
#include <string>

int main() {
    std::string s = "hello world";
    bool has = s.find("world") != std::string::npos;
    std::cout << std::boolalpha << has << '\n';
    return 0;
}

マッチオブジェクトの再利用

繰り返し検索するときは、毎回新しいオブジェクトを割り当てるのではなく、1つのstd::smatchを再利用してオーバーヘッドを減らします。

#include <iostream>
#include <regex>
#include <vector>

int main() {
    static const std::regex p("(\\d+)");
    std::smatch m;
    std::vector<std::string> data = {"x1", "y22", "z333"};
    for (const auto& s : data) {
        if (std::regex_search(s, m, p)) std::cout << m[1] << ' ';
    }
    std::cout << '\n';
    return 0;
}

パターンを絞り込む

具体的な文字クラスは、何でも一致するドットよりも高速かつ安全に一致します。数字を表す場合は、.ではなく[0-9]を優先してください。

#include <iostream>
#include <regex>

int main() {
    std::regex specific("[0-9]{2}");
    std::cout << std::boolalpha;
    std::cout << std::regex_match("42", specific) << '\n';
    std::cout << std::regex_match("ab", specific) << '\n';
    return 0;
}

最適化の前に計測する

<chrono>について学んだことを組み合わせ、変更を採用する前に正規表現の使用時間を計測して、本当に効果があることを確認してください。

#include <iostream>
#include <regex>
#include <chrono>

int main() {
    using namespace std::chrono;
    static const std::regex p("\\d+");
    auto s = steady_clock::now();
    int hits = 0;
    for (int i = 0; i < 1000; ++i) {
        if (std::regex_search(std::string("x") + std::to_string(i), p)) ++hits;
    }
    auto e = steady_clock::now();
    std::cout << "hits=" << hits << ", timed: " << ((e - s).count() >= 0) << '\n';
    return 0;
}

理解度チェック

正規表現のパフォーマンスについての理解度を確認してください。

まとめ

正規表現を効率的に使用する方法を学びました。

  • パターンは一度、できればstatic constとしてコンパイルし、再利用します
  • 適切な文法を選び、optimizeの使用を検討します
  • 壊滅的なバックトラッキングを避け、可能な場合はアンカーを使用します
  • 固定部分文字列にはfindのような単純なツールを使用し、変更の効果を計測します

これで正規表現コースは完了です。

無料で開始

AI チューターと学ぶ C++ — 無料

ブラウザでリアルコードを書いて実行し、24/7 の AI チューターから瞬時にサポートを受け、ウェブまたはアプリで続きから学習できます。

コース
51
レッスン
203

よくある質問

「パフォーマンスに関する注意点」レッスンは無料ですか?

はい。「パフォーマンスに関する注意点」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、C++ Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 C++ Academyコースには全4レッスンが含まれています。

「パフォーマンスに関する注意点」で何を学びますか?

パターンを適切にコンパイルします ブラウザで直接実行するハンズオンコードでC++ Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

C++ Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのC++ Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン4/4です。

「パフォーマンスに関する注意点」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このC++ Academyレッスンでコードを書いて実行できますか?

はい。すべてのC++ Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. regexの基礎
  2. キャプチャグループ
  3. 検索と置換
  4. パフォーマンスに関する注意点
← C++ Academyに戻る