【完全ガイド】std::regexによる効率的な文字列処理 – 実践的な実装例と注意点

std::regex の基礎知識

正規表現ライブラリの進化と特徴

C++11で導入されたstd::regexは、現代のC++プログラミングにおける文字列処理の重要な要素となっています。このライブラリの進化と特徴について詳しく見ていきましょう。

歴史的背景

  1. 導入以前の状況
  • C++11以前は、標準ライブラリに正規表現機能が存在しませんでした
  • 開発者はBoostやPCREなどのサードパーティライブラリに依存していました
  • プラットフォーム間の互換性に課題がありました
  1. C++11での導入
  • 標準ライブラリの一部として正規表現機能が統合されました
  • ECMAScript文法をベースとした使いやすい設計が採用されました
  • クロスプラットフォームでの一貫した動作が保証されました

主要な特徴

  1. 豊富な正規表現文法のサポート
   // 複数の正規表現文法をサポート
   std::regex pattern("pattern", std::regex_constants::ECMAScript);
   std::regex pattern_basic("pattern", std::regex_constants::basic);
   std::regex pattern_extended("pattern", std::regex_constants::extended);
  1. 強力なマッチング機能
   // 完全マッチング、部分マッチング、置換など様々な操作が可能
   std::string text = "Hello, World!";
   std::regex pattern("World");
   bool found = std::regex_search(text, pattern);  // 部分マッチング
  1. Unicode対応
  • ワイド文字列(wstring)のサポート
  • マルチバイト文字の適切な処理
  • 国際化対応の容易さ

std::regex が選ばれる理由

1. 標準化されたインターフェース

std::regexは、C++標準ライブラリの一部として以下のような利点を提供します:

  • 移植性の高さ
  // どのプラットフォームでも同じコードが動作
  std::regex date_pattern("\\d{4}-\\d{2}-\\d{2}");
  std::string date = "2024-01-15";
  if (std::regex_match(date, date_pattern)) {
      // 日付の検証が可能
  }
  • 一貫した動作の保証
  • 標準規格に準拠した実装
  • プラットフォーム間での動作の一貫性
  • コンパイラの最適化サポート

2. 使いやすいAPI設計

// シンプルな文字列マッチング
std::string text = "連絡先: contact@example.com";
std::regex email_pattern("[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}");
std::smatch matches;
if (std::regex_search(text, matches, email_pattern)) {
    std::cout << "Found email: " << matches[0] << std::endl;
}

3. 豊富な機能セット

  • イテレータベースの操作
  // 文字列内の全マッチを検索
  std::string text = "ID: 123, ID: 456, ID: 789";
  std::regex id_pattern("ID: (\\d+)");
  auto words_begin = std::sregex_iterator(text.begin(), text.end(), id_pattern);
  auto words_end = std::sregex_iterator();
  • 柔軟なマッチング制御
  // フラグによる動作のカスタマイズ
  std::regex pattern("pattern", 
      std::regex_constants::ECMAScript | 
      std::regex_constants::icase);  // 大文字小文字を区別しない

4. パフォーマンスと信頼性

  • コンパイル時のパターン検証
  • 最適化された実行時パフォーマンス
  • メモリ安全な操作の保証

これらの特徴により、std::regexは以下のような場面で特に有効です:

  • 入力データのバリデーション
  • テキストファイルの解析
  • 設定ファイルの処理
  • ログファイルの分析
  • フォーマット変換処理

標準ライブラリの一部として提供される信頼性と、十分な機能セットにより、多くの開発者がstd::regexを選択しています。特に、クロスプラットフォーム開発や長期的なメンテナンスを考慮する場合、標準ライブラリの使用は賢明な選択となります。

std::regexの実践的な使い方

基本的なパターンマッチングの実装方法

std::regexを使用した基本的なパターンマッチングには、主に以下の3つの方法があります:

  1. std::regex_match: 文字列全体が正規表現パターンに一致するか確認
bool validateEmail(const std::string& email) {
    // メールアドレスの基本的な検証パターン
    std::regex pattern(
        "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$",
        std::regex_constants::ECMAScript
    );

    // 文字列全体がパターンに一致するか確認
    return std::regex_match(email, pattern);
}
  1. std::regex_search: パターンに一致する部分を検索
void findPhoneNumbers(const std::string& text) {
    // 電話番号パターン(例: 000-0000-0000)
    std::regex pattern("\\d{3}-\\d{4}-\\d{4}");
    std::smatch matches;
    std::string::const_iterator searchStart(text.cbegin());

    // すべての一致を検索
    while (std::regex_search(searchStart, text.cend(), matches, pattern)) {
        std::cout << "Found: " << matches[0] << std::endl;
        searchStart = matches.suffix().first;
    }
}
  1. std::regex_iterator: 一致するすべての箇所を反復処理
void findAllMatches(const std::string& text, const std::regex& pattern) {
    // イテレータを使用して全一致を処理
    auto words_begin = std::sregex_iterator(
        text.begin(), text.end(), pattern
    );
    auto words_end = std::sregex_iterator();

    for (std::sregex_iterator i = words_begin; i != words_end; ++i) {
        std::smatch match = *i;
        std::cout << match.str() << std::endl;
    }
}

文字列置換機能の活用テクニック

std::regexによる文字列置換は、std::regex_replaceを使用して実装できます:

  1. 基本的な置換
std::string formatDates(const std::string& text) {
    // YYYY/MM/DD形式をYYYY-MM-DD形式に変換
    std::regex date_pattern("(\\d{4})/(\\d{2})/(\\d{2})");
    return std::regex_replace(
        text,
        date_pattern,
        "$1-$2-$3"  // キャプチャグループを使用
    );
}
  1. 条件付き置換
std::string maskSensitiveData(const std::string& text) {
    // クレジットカード番号をマスク
    std::regex card_pattern("(\\d{4})-?(\\d{4})-?(\\d{4})-?(\\d{4})");
    return std::regex_replace(
        text,
        card_pattern,
        "$1-XXXX-XXXX-$4"
    );
}
  1. 高度な置換処理
std::string formatText(const std::string& text) {
    // 複数の置換ルールを適用
    std::regex patterns[] = {
        std::regex("\\s+"),                    // 連続する空白
        std::regex("^\\s+|\\s+$"),            // 先頭・末尾の空白
        std::regex("([.!?])([^\\s])")         // 句読点後のスペース
    };

    std::string result = text;
    // 単一の空白に置換
    result = std::regex_replace(result, patterns[0], " ");
    // 先頭・末尾の空白を削除
    result = std::regex_replace(result, patterns[1], "");
    // 句読点後にスペースを追加
    result = std::regex_replace(result, patterns[2], "$1 $2");

    return result;
}

マルチバイト文字での利用方法

マルチバイト文字(日本語など)を扱う場合は、以下の点に注意が必要です:

  1. 文字エンコーディングの考慮
#include <codecvt>
#include <locale>

std::wstring convertToWideString(const std::string& str) {
    // UTF-8からワイド文字列への変換
    std::wstring_convert<std::codecvt_utf8<wchar_t>> converter;
    return converter.from_bytes(str);
}

void processJapaneseText(const std::string& text) {
    // ワイド文字列に変換
    std::wstring wide_text = convertToWideString(text);

    // 日本語を含むパターン
    std::wregex pattern(L"[一-龯]");

    // マッチング処理
    std::wsmatch matches;
    if (std::regex_search(wide_text, matches, pattern)) {
        std::wcout << L"漢字が見つかりました: " << matches[0] << std::endl;
    }
}
  1. Unicode対応パターンの使用
void validateJapaneseAddress(const std::string& address) {
    // 日本の住所パターン
    std::wregex pattern(
        L"〒?\\d{3}-?\\d{4}\\s*[都道府県]"
    );

    std::wstring wide_address = convertToWideString(address);
    if (std::regex_search(wide_address, pattern)) {
        std::cout << "Valid Japanese address format" << std::endl;
    }
}
  1. パフォーマンス最適化
class JapaneseTextProcessor {
private:
    std::wregex pattern_;
    std::wstring_convert<std::codecvt_utf8<wchar_t>> converter_;

public:
    JapaneseTextProcessor() : 
        pattern_(L"[ぁ-んァ-ン一-龯]") {
        // パターンを事前にコンパイル
    }

    bool containsJapanese(const std::string& text) {
        try {
            std::wstring wide_text = converter_.from_bytes(text);
            return std::regex_search(wide_text, pattern_);
        } catch (const std::exception& e) {
            std::cerr << "Error processing text: " << e.what() << std::endl;
            return false;
        }
    }
};

実装時の注意点:

  1. エンコーディング関連
  • UTF-8エンコーディングの使用を推奨
  • 文字コード変換時のエラー処理が重要
  • ロケール設定の考慮
  1. パフォーマンス考慮
  • パターンの事前コンパイル
  • 不要な変換の回避
  • メモリ使用量の最適化
  1. エラーハンドリング
  • 文字コード変換エラーの処理
  • 無効なパターンへの対応
  • メモリ不足への対処

これらの実装例と注意点を踏まえることで、効率的で堅牢な正規表現処理を実現できます。

パフォーマンス最適化のポイント

コンパイル済み正規表現の活用

正規表現のコンパイルは比較的コストの高い操作です。以下のテクニックを活用することで、パフォーマンスを大幅に改善できます。

  1. パターンの再利用
// 悪い例:ループ内でパターンを毎回作成
void badExample(const std::vector<std::string>& inputs) {
    for (const auto& input : inputs) {
        std::regex pattern("\\d{4}-\\d{2}-\\d{2}"); // 毎回コンパイル
        if (std::regex_match(input, pattern)) {
            // 処理
        }
    }
}

// 良い例:パターンを事前にコンパイル
void goodExample(const std::vector<std::string>& inputs) {
    static const std::regex pattern("\\d{4}-\\d{2}-\\d{2}"); // 一度だけコンパイル
    for (const auto& input : inputs) {
        if (std::regex_match(input, pattern)) {
            // 処理
        }
    }
}
  1. 最適化フラグの活用
class PatternMatcher {
private:
    std::regex pattern_;

public:
    PatternMatcher() : pattern_("\\w+@\\w+\\.\\w+",
        std::regex_constants::optimize |  // 実行速度を優先
        std::regex_constants::ECMAScript) {
    }

    bool isMatch(const std::string& text) {
        return std::regex_match(text, pattern_);
    }
};

メモリ使用量の最適化テクニック

メモリ使用量を最適化するために、以下の戦略を採用できます:

  1. イテレータの効率的な使用
class TextAnalyzer {
private:
    std::regex pattern_;

public:
    TextAnalyzer(const std::string& pattern) : pattern_(pattern) {}

    // メモリ効率の良い実装
    void analyzeStream(std::istream& input) {
        std::string line;
        while (std::getline(input, line)) {
            auto words_begin = std::sregex_iterator(
                line.begin(), line.end(), pattern_);
            auto words_end = std::sregex_iterator();

            for (auto it = words_begin; it != words_end; ++it) {
                // 一致を1つずつ処理
                processMatch(*it);
            }
            // 各行を処理後にメモリを解放
            line.clear();
            line.shrink_to_fit();
        }
    }

private:
    void processMatch(const std::smatch& match) {
        // マッチ処理
    }
};
  1. メモリプール使用の検討
#include <memory_resource>

class RegexProcessor {
private:
    std::pmr::monotonic_buffer_resource resource_{4096}; // 4KBのバッファ
    std::pmr::string_view text_;
    std::regex pattern_;

public:
    RegexProcessor(std::string_view text, const std::regex& pattern)
        : text_(text), pattern_(pattern) {}

    void process() {
        std::pmr::vector<std::pmr::string> matches(&resource_);
        // マッチング処理
        // リソースは自動的に解放される
    }
};

処理速度を向上させるベストプラクティス

  1. パターンの最適化
class OptimizedMatcher {
public:
    // 非効率なパターン
    static const std::regex slow_pattern;
    // 最適化されたパターン
    static const std::regex fast_pattern;

    static bool matchEmail(const std::string& email) {
        // 最適化されたパターンを使用
        return std::regex_match(email, fast_pattern);
    }
};

// 非効率なパターン(バックトラッキングが多発)
const std::regex OptimizedMatcher::slow_pattern(
    "([a-zA-Z0-9]+.*)+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}"
);

// 最適化されたパターン(バックトラッキングを最小化)
const std::regex OptimizedMatcher::fast_pattern(
    "[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}"
);
  1. 並列処理の活用
#include <execution>
#include <algorithm>

class ParallelRegexProcessor {
private:
    std::regex pattern_;
    std::vector<std::string> inputs_;

public:
    ParallelRegexProcessor(const std::string& pattern,
                          const std::vector<std::string>& inputs)
        : pattern_(pattern), inputs_(inputs) {}

    std::vector<bool> processInParallel() {
        std::vector<bool> results(inputs_.size());

        // 並列処理の実行
        std::transform(
            std::execution::par,  // 並列実行ポリシー
            inputs_.begin(), inputs_.end(),
            results.begin(),
            [this](const std::string& input) {
                return std::regex_match(input, this->pattern_);
            }
        );

        return results;
    }
};
  1. キャッシュの活用
class RegexCache {
private:
    struct CacheEntry {
        std::string pattern;
        std::regex compiled_pattern;
        std::chrono::system_clock::time_point last_used;

        CacheEntry(const std::string& p)
            : pattern(p), compiled_pattern(p),
              last_used(std::chrono::system_clock::now()) {}
    };

    std::unordered_map<std::string, CacheEntry> cache_;
    static const size_t MAX_CACHE_SIZE = 100;

public:
    const std::regex& getPattern(const std::string& pattern) {
        auto it = cache_.find(pattern);
        if (it != cache_.end()) {
            it->second.last_used = std::chrono::system_clock::now();
            return it->second.compiled_pattern;
        }

        if (cache_.size() >= MAX_CACHE_SIZE) {
            removeOldestEntry();
        }

        auto [inserted_it, success] = cache_.emplace(
            pattern, CacheEntry(pattern));
        return inserted_it->second.compiled_pattern;
    }

private:
    void removeOldestEntry() {
        auto oldest = std::min_element(
            cache_.begin(), cache_.end(),
            [](const auto& a, const auto& b) {
                return a.second.last_used < b.second.last_used;
            }
        );
        if (oldest != cache_.end()) {
            cache_.erase(oldest);
        }
    }
};

パフォーマンス最適化の重要なポイント:

  1. コンパイル済みパターンの再利用
  • 静的なパターンの使用
  • パターンのキャッシング
  • 最適化フラグの活用
  1. メモリ管理の最適化
  • 効率的なメモリアロケーション
  • リソースの適切な解放
  • メモリプールの使用
  1. 処理速度の改善
  • パターンの最適化
  • 並列処理の活用
  • キャッシュ戦略の実装

これらの最適化テクニックを適切に組み合わせることで、std::regexを使用した処理のパフォーマンスを大幅に向上させることができます。

エラーハンドリングとデバッグ

一般的なエラーパターンと対処法

std::regexを使用する際に発生する主要なエラーと、その効果的な対処方法について解説します。

  1. 構文エラーの処理
class RegexValidator {
public:
    static bool isValidPattern(const std::string& pattern) {
        try {
            std::regex re(pattern);
            return true;
        } catch (const std::regex_error& e) {
            std::cerr << "Invalid regex pattern: " << e.what() << std::endl;
            std::cerr << "Error code: " << e.code() << std::endl;

            // エラーコードに基づく詳細な診断
            switch (e.code()) {
                case std::regex_constants::error_collate:
                    std::cerr << "Invalid collating element request" << std::endl;
                    break;
                case std::regex_constants::error_ctype:
                    std::cerr << "Invalid character class" << std::endl;
                    break;
                case std::regex_constants::error_escape:
                    std::cerr << "Invalid escape character or trailing escape" << std::endl;
                    break;
                case std::regex_constants::error_backref:
                    std::cerr << "Invalid back reference" << std::endl;
                    break;
                case std::regex_constants::error_brack:
                    std::cerr << "Mismatched bracket ([ or ])" << std::endl;
                    break;
                case std::regex_constants::error_paren:
                    std::cerr << "Mismatched parentheses (( or ))" << std::endl;
                    break;
                case std::regex_constants::error_brace:
                    std::cerr << "Mismatched brace ({ or })" << std::endl;
                    break;
                case std::regex_constants::error_badbrace:
                    std::cerr << "Invalid range inside brace expression" << std::endl;
                    break;
                case std::regex_constants::error_range:
                    std::cerr << "Invalid character range" << std::endl;
                    break;
                case std::regex_constants::error_space:
                    std::cerr << "Insufficient memory" << std::endl;
                    break;
                case std::regex_constants::error_badrepeat:
                    std::cerr << "Invalid use of repetition operators" << std::endl;
                    break;
                case std::regex_constants::error_complexity:
                    std::cerr << "Pattern too complex" << std::endl;
                    break;
                case std::regex_constants::error_stack:
                    std::cerr << "Stack space overflow" << std::endl;
                    break;
                default:
                    std::cerr << "Unknown error" << std::endl;
            }
            return false;
        }
    }
};
  1. 実行時エラーの処理
class SafeRegexMatcher {
private:
    std::regex pattern_;
    bool is_valid_;

public:
    SafeRegexMatcher(const std::string& pattern_str) : is_valid_(false) {
        try {
            pattern_ = std::regex(pattern_str);
            is_valid_ = true;
        } catch (const std::regex_error& e) {
            std::cerr << "Failed to compile regex: " << e.what() << std::endl;
        }
    }

    std::optional<bool> match(const std::string& text) {
        if (!is_valid_) {
            return std::nullopt;
        }

        try {
            return std::regex_match(text, pattern_);
        } catch (const std::exception& e) {
            std::cerr << "Match operation failed: " << e.what() << std::endl;
            return std::nullopt;
        }
    }
};
  1. メモリ関連エラーの処理
class MemoryAwareRegex {
private:
    static constexpr size_t MAX_INPUT_LENGTH = 1024 * 1024; // 1MB

public:
    static std::optional<std::vector<std::string>> findMatches(
        const std::string& text,
        const std::regex& pattern
    ) {
        if (text.length() > MAX_INPUT_LENGTH) {
            std::cerr << "Input text too large" << std::endl;
            return std::nullopt;
        }

        try {
            std::vector<std::string> matches;
            auto words_begin = std::sregex_iterator(
                text.begin(), text.end(), pattern
            );
            auto words_end = std::sregex_iterator();

            // 予想される最大マッチ数で事前にメモリを確保
            matches.reserve(std::distance(words_begin, words_end));

            for (auto i = words_begin; i != words_end; ++i) {
                matches.push_back(i->str());
            }
            return matches;
        } catch (const std::bad_alloc& e) {
            std::cerr << "Memory allocation failed: " << e.what() << std::endl;
            return std::nullopt;
        }
    }
};

デバッグ時の効率的なアプローチ

  1. パターンのデバッグ支援クラス
class RegexDebugger {
public:
    static void debugPattern(const std::string& pattern, 
                           const std::string& test_input) {
        std::cout << "Debug Information:" << std::endl;
        std::cout << "Pattern: " << pattern << std::endl;
        std::cout << "Test input: " << test_input << std::endl;

        try {
            std::regex re(pattern);
            std::smatch matches;
            bool found = std::regex_search(test_input, matches, re);

            std::cout << "Match found: " << (found ? "Yes" : "No") << std::endl;
            if (found) {
                std::cout << "Number of matches: " << matches.size() << std::endl;
                for (size_t i = 0; i < matches.size(); ++i) {
                    std::cout << "Group " << i << ": '" << matches[i] 
                              << "' (Position: " << matches.position(i) 
                              << ", Length: " << matches.length(i) << ")" 
                              << std::endl;
                }
            }
        } catch (const std::regex_error& e) {
            std::cout << "Regex Error: " << e.what() << std::endl;
            std::cout << "Error Code: " << e.code() << std::endl;
        }
    }

    static void visualizeMatches(const std::string& input,
                               const std::regex& pattern) {
        std::string result = input;
        auto it = std::sregex_iterator(input.begin(), input.end(), pattern);
        auto end = std::sregex_iterator();

        // マッチした部分を可視化
        while (it != end) {
            const auto& match = *it;
            size_t pos = match.position();
            size_t len = match.length();
            result.insert(pos + len, "]");
            result.insert(pos, "[");
            ++it;
        }

        std::cout << "Visualized matches: " << result << std::endl;
    }
};
  1. ロギング機能の実装
class RegexLogger {
private:
    static std::ofstream log_file_;
    static std::mutex log_mutex_;

public:
    static void initLogger(const std::string& filename) {
        log_file_.open(filename, std::ios::app);
    }

    static void logOperation(const std::string& pattern,
                           const std::string& input,
                           const std::string& operation,
                           bool success) {
        std::lock_guard<std::mutex> lock(log_mutex_);
        if (log_file_.is_open()) {
            auto now = std::chrono::system_clock::now();
            auto time = std::chrono::system_clock::to_time_t(now);

            log_file_ << "Time: " << std::ctime(&time)
                     << "Operation: " << operation << std::endl
                     << "Pattern: " << pattern << std::endl
                     << "Input: " << input << std::endl
                     << "Result: " << (success ? "Success" : "Failed")
                     << std::endl << std::endl;
        }
    }

    static void closeLogger() {
        if (log_file_.is_open()) {
            log_file_.close();
        }
    }
};

std::ofstream RegexLogger::log_file_;
std::mutex RegexLogger::log_mutex_;

主要なデバッグポイント:

  1. エラー診断
  • エラーコードの解析
  • エラーメッセージの詳細化
  • スタックトレースの活用
  1. パターン検証
  • 段階的なパターンテスト
  • マッチ結果の可視化
  • グループキャプチャの確認
  1. パフォーマンス分析
  • 実行時間の計測
  • メモリ使用量の監視
  • ボトルネックの特定

これらのエラーハンドリングとデバッグ手法を適切に組み合わせることで、std::regexを使用したコードの信頼性と保守性を大幅に向上させることができます。

実践的な用途ケース集

ログ解析での活用例

ログファイルの解析は、std::regexの最も一般的で実用的な用途の一つです。

  1. エラーログの解析
class LogAnalyzer {
private:
    // エラーログのパターン
    std::regex error_pattern_;
    std::regex timestamp_pattern_;

public:
    LogAnalyzer() : 
        error_pattern_("\\[(ERROR|CRITICAL)\\]\\s+(.*?)\\s+at\\s+(\\d{4}-\\d{2}-\\d{2}\\s+\\d{2}:\\d{2}:\\d{2})"),
        timestamp_pattern_("\\d{4}-\\d{2}-\\d{2}\\s+\\d{2}:\\d{2}:\\d{2}") {}

    struct LogEntry {
        std::string severity;
        std::string message;
        std::string timestamp;
    };

    std::vector<LogEntry> analyzeLogFile(const std::string& filename) {
        std::vector<LogEntry> errors;
        std::ifstream file(filename);
        std::string line;

        while (std::getline(file, line)) {
            std::smatch matches;
            if (std::regex_search(line, matches, error_pattern_)) {
                LogEntry entry{
                    matches[1].str(),  // severity
                    matches[2].str(),  // message
                    matches[3].str()   // timestamp
                };
                errors.push_back(entry);
            }
        }
        return errors;
    }

    // 特定の時間範囲のエラーを抽出
    std::vector<LogEntry> filterByTimeRange(
        const std::vector<LogEntry>& entries,
        const std::string& start_time,
        const std::string& end_time
    ) {
        std::vector<LogEntry> filtered;
        for (const auto& entry : entries) {
            if (entry.timestamp >= start_time && 
                entry.timestamp <= end_time) {
                filtered.push_back(entry);
            }
        }
        return filtered;
    }
};
  1. アクセスログの分析
class AccessLogAnalyzer {
private:
    // Apache形式のアクセスログパターン
    std::regex access_pattern_;

public:
    AccessLogAnalyzer() : 
        access_pattern_(R"((\d+\.\d+\.\d+\.\d+)\s+-\s+-\s+\[(.*?)\]\s+"([^"]+)"\s+(\d+)\s+(\d+))") {}

    struct AccessEntry {
        std::string ip;
        std::string timestamp;
        std::string request;
        int status_code;
        int bytes_sent;
    };

    // アクセスログの解析と統計情報の生成
    std::map<std::string, int> analyzeIPAddresses(const std::string& filename) {
        std::map<std::string, int> ip_counts;
        std::ifstream file(filename);
        std::string line;

        while (std::getline(file, line)) {
            std::smatch matches;
            if (std::regex_search(line, matches, access_pattern_)) {
                ip_counts[matches[1].str()]++;
            }
        }
        return ip_counts;
    }
};

設定ファイルのパース処理

設定ファイルの解析と値の抽出にstd::regexを活用できます。

  1. INIファイルパーサー
class INIParser {
private:
    std::regex section_pattern_;
    std::regex key_value_pattern_;
    std::map<std::string, std::map<std::string, std::string>> config_;

public:
    INIParser() :
        section_pattern_(R"(\[([^\]]+)\])"),
        key_value_pattern_(R"(([^=\s]+)\s*=\s*([^\s].*))")
    {}

    bool parseFile(const std::string& filename) {
        std::ifstream file(filename);
        std::string line;
        std::string current_section;

        while (std::getline(file, line)) {
            // 空行とコメントをスキップ
            if (line.empty() || line[0] == ';') continue;

            std::smatch matches;
            if (std::regex_match(line, matches, section_pattern_)) {
                current_section = matches[1].str();
            }
            else if (std::regex_match(line, matches, key_value_pattern_)) {
                config_[current_section][matches[1].str()] = matches[2].str();
            }
        }
        return true;
    }

    std::string getValue(const std::string& section, 
                        const std::string& key,
                        const std::string& default_value = "") {
        auto section_it = config_.find(section);
        if (section_it != config_.end()) {
            auto key_it = section_it->second.find(key);
            if (key_it != section_it->second.end()) {
                return key_it->second;
            }
        }
        return default_value;
    }
};

入力データのバリデーション実装

ユーザー入力やデータファイルの検証にstd::regexを活用できます。

  1. フォームバリデータ
class FormValidator {
private:
    std::regex email_pattern_;
    std::regex phone_pattern_;
    std::regex date_pattern_;
    std::regex password_pattern_;

public:
    FormValidator() :
        email_pattern_("[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}"),
        phone_pattern_("\\+?\\d{1,4}[-.\\s]?\\(?\\d{1,3}\\)?[-.\\s]?\\d{1,4}[-.\\s]?\\d{1,4}[-.\\s]?\\d{1,9}"),
        date_pattern_("\\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\\d|3[01])"),
        password_pattern_("^(?=.*[A-Za-z])(?=.*\\d)(?=.*[@$!%*#?&])[A-Za-z\\d@$!%*#?&]{8,}$")
    {}

    struct ValidationResult {
        bool is_valid;
        std::string error_message;
    };

    ValidationResult validateEmail(const std::string& email) {
        if (std::regex_match(email, email_pattern_)) {
            return {true, ""};
        }
        return {false, "Invalid email format"};
    }

    ValidationResult validatePhone(const std::string& phone) {
        if (std::regex_match(phone, phone_pattern_)) {
            return {true, ""};
        }
        return {false, "Invalid phone number format"};
    }

    ValidationResult validateDate(const std::string& date) {
        if (std::regex_match(date, date_pattern_)) {
            return {true, ""};
        }
        return {false, "Invalid date format (YYYY-MM-DD)"};
    }

    ValidationResult validatePassword(const std::string& password) {
        if (std::regex_match(password, password_pattern_)) {
            return {true, ""};
        }
        return {false, "Password must contain at least 8 characters, "
                      "including letters, numbers, and special characters"};
    }
};
  1. データファイルバリデータ
class DataFileValidator {
private:
    std::regex csv_line_pattern_;
    std::regex json_pattern_;

public:
    DataFileValidator() :
        csv_line_pattern_(R"((?:[^,\\"]|\"(?:[^\"]|\\\")*\")*(?:,(?:[^,\\"]|\"(?:[^\"]|\\\")*\")*)*\r?\n?)"),
        json_pattern_(R"(\{(?:[^{}]|(?R))*\})")
    {}

    bool validateCSVLine(const std::string& line) {
        return std::regex_match(line, csv_line_pattern_);
    }

    bool validateJSONObject(const std::string& json) {
        return std::regex_match(json, json_pattern_);
    }

    // CSVファイル全体の検証
    std::vector<size_t> validateCSVFile(const std::string& filename) {
        std::vector<size_t> invalid_lines;
        std::ifstream file(filename);
        std::string line;
        size_t line_number = 0;

        while (std::getline(file, line)) {
            line_number++;
            if (!validateCSVLine(line)) {
                invalid_lines.push_back(line_number);
            }
        }
        return invalid_lines;
    }
};

実践的な使用におけるポイント:

  1. パフォーマンスの考慮
  • パターンの事前コンパイル
  • 適切なバッファサイズの設定
  • メモリ使用量の最適化
  1. エラー処理
  • 適切な例外処理
  • エラーメッセージの明確化
  • フォールバック処理の実装
  1. メンテナンス性
  • パターンの集中管理
  • ドキュメントの整備
  • テストケースの作成

これらの実践的な用途ケースを参考に、プロジェクトの要件に応じて適切にカスタマイズすることで、std::regexの機能を最大限に活用できます。