std::regex の基礎知識
正規表現ライブラリの進化と特徴
C++11で導入されたstd::regexは、現代のC++プログラミングにおける文字列処理の重要な要素となっています。このライブラリの進化と特徴について詳しく見ていきましょう。
歴史的背景
- 導入以前の状況
- C++11以前は、標準ライブラリに正規表現機能が存在しませんでした
- 開発者はBoostやPCREなどのサードパーティライブラリに依存していました
- プラットフォーム間の互換性に課題がありました
- C++11での導入
- 標準ライブラリの一部として正規表現機能が統合されました
- ECMAScript文法をベースとした使いやすい設計が採用されました
- クロスプラットフォームでの一貫した動作が保証されました
主要な特徴
- 豊富な正規表現文法のサポート
// 複数の正規表現文法をサポート
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);
- 強力なマッチング機能
// 完全マッチング、部分マッチング、置換など様々な操作が可能
std::string text = "Hello, World!";
std::regex pattern("World");
bool found = std::regex_search(text, pattern); // 部分マッチング
- 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つの方法があります:
- 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);
}
- 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;
}
}
- 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を使用して実装できます:
- 基本的な置換
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" // キャプチャグループを使用
);
}
- 条件付き置換
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"
);
}
- 高度な置換処理
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;
}
マルチバイト文字での利用方法
マルチバイト文字(日本語など)を扱う場合は、以下の点に注意が必要です:
- 文字エンコーディングの考慮
#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;
}
}
- 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;
}
}
- パフォーマンス最適化
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;
}
}
};
実装時の注意点:
- エンコーディング関連
- UTF-8エンコーディングの使用を推奨
- 文字コード変換時のエラー処理が重要
- ロケール設定の考慮
- パフォーマンス考慮
- パターンの事前コンパイル
- 不要な変換の回避
- メモリ使用量の最適化
- エラーハンドリング
- 文字コード変換エラーの処理
- 無効なパターンへの対応
- メモリ不足への対処
これらの実装例と注意点を踏まえることで、効率的で堅牢な正規表現処理を実現できます。
パフォーマンス最適化のポイント
コンパイル済み正規表現の活用
正規表現のコンパイルは比較的コストの高い操作です。以下のテクニックを活用することで、パフォーマンスを大幅に改善できます。
- パターンの再利用
// 悪い例:ループ内でパターンを毎回作成
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)) {
// 処理
}
}
}
- 最適化フラグの活用
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_);
}
};
メモリ使用量の最適化テクニック
メモリ使用量を最適化するために、以下の戦略を採用できます:
- イテレータの効率的な使用
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) {
// マッチ処理
}
};
- メモリプール使用の検討
#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_);
// マッチング処理
// リソースは自動的に解放される
}
};
処理速度を向上させるベストプラクティス
- パターンの最適化
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,}"
);
- 並列処理の活用
#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;
}
};
- キャッシュの活用
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);
}
}
};
パフォーマンス最適化の重要なポイント:
- コンパイル済みパターンの再利用
- 静的なパターンの使用
- パターンのキャッシング
- 最適化フラグの活用
- メモリ管理の最適化
- 効率的なメモリアロケーション
- リソースの適切な解放
- メモリプールの使用
- 処理速度の改善
- パターンの最適化
- 並列処理の活用
- キャッシュ戦略の実装
これらの最適化テクニックを適切に組み合わせることで、std::regexを使用した処理のパフォーマンスを大幅に向上させることができます。
エラーハンドリングとデバッグ
一般的なエラーパターンと対処法
std::regexを使用する際に発生する主要なエラーと、その効果的な対処方法について解説します。
- 構文エラーの処理
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;
}
}
};
- 実行時エラーの処理
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;
}
}
};
- メモリ関連エラーの処理
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;
}
}
};
デバッグ時の効率的なアプローチ
- パターンのデバッグ支援クラス
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;
}
};
- ロギング機能の実装
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_;
主要なデバッグポイント:
- エラー診断
- エラーコードの解析
- エラーメッセージの詳細化
- スタックトレースの活用
- パターン検証
- 段階的なパターンテスト
- マッチ結果の可視化
- グループキャプチャの確認
- パフォーマンス分析
- 実行時間の計測
- メモリ使用量の監視
- ボトルネックの特定
これらのエラーハンドリングとデバッグ手法を適切に組み合わせることで、std::regexを使用したコードの信頼性と保守性を大幅に向上させることができます。
実践的な用途ケース集
ログ解析での活用例
ログファイルの解析は、std::regexの最も一般的で実用的な用途の一つです。
- エラーログの解析
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;
}
};
- アクセスログの分析
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を活用できます。
- 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を活用できます。
- フォームバリデータ
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"};
}
};
- データファイルバリデータ
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;
}
};
実践的な使用におけるポイント:
- パフォーマンスの考慮
- パターンの事前コンパイル
- 適切なバッファサイズの設定
- メモリ使用量の最適化
- エラー処理
- 適切な例外処理
- エラーメッセージの明確化
- フォールバック処理の実装
- メンテナンス性
- パターンの集中管理
- ドキュメントの整備
- テストケースの作成
これらの実践的な用途ケースを参考に、プロジェクトの要件に応じて適切にカスタマイズすることで、std::regexの機能を最大限に活用できます。