Boost

【C++】Boost.Regexで文字列を正規表現検索してマッチ結果を取得する方法

C++でBoost.Regexを使って文字列に正規表現検索をかけると、boost::regexでパターンを定義し、boost::regex_searchboost::regex_matchでマッチを判定できます。

生文字列リテラルを活用してエスケープ負荷を軽減しつつ柔軟な検索が可能です。

正規表現パターンの構築

正規表現を用いた文字列検索を行う際には、まずパターンとなる正規表現オブジェクトを正しく構築することが重要です。

Boost.Regexでは、boost::regexクラスを使って正規表現のパターンを定義します。

ここでは、その具体的な方法と、正規表現の動作を制御するためのオプション設定について詳しく解説します。

boost::regexコンストラクタ

boost::regexは、正規表現のパターンを表すクラスです。

これをインスタンス化することで、文字列検索や置換に使用できる正規表現オブジェクトが作成されます。

コンストラクタには、正規表現のパターンを表す文字列を渡します。

#include <boost/regex.hpp>
#include <string>
// 正規表現パターンを定義
std::string pattern = "abc.*123";
// boost::regexオブジェクトの作成
boost::regex regexPattern(pattern);

この例では、"abc.*123"というパターンを持つ正規表現オブジェクトを作成しています。

boost::regexのコンストラクタは、パターン文字列を受け取り、そのパターンに基づいた正規表現を内部的にコンパイルします。

正規表現オプションの指定

boost::regexのコンストラクタには、オプションを指定することも可能です。

これにより、正規表現の動作を細かく制御できます。

オプションはboost::regex_constants名前空間に定義されており、ビットフラグとして複数指定できます。

#include <boost/regex.hpp>
#include <string>
// 正規表現のパターン
std::string pattern = "abc.*123";
// オプションの指定(複数指定可能)
boost::regex regexPattern(pattern, boost::regex_constants::icase | boost::regex_constants::optimize);

この例では、icaseoptimizeの2つのオプションを指定しています。

次に、それぞれのオプションについて詳しく解説します。

大文字小文字を無視する指定(boost::regex_constants::icase)

boost::regex_constants::icaseを指定すると、大文字と小文字の区別をしない検索が可能になります。

たとえば、パターンが"abc"の場合、"ABC""aBc"といった文字列ともマッチします。

boost::regex regexPattern("abc", boost::regex_constants::icase);

この設定は、ケースインセンシティブな検索を行いたい場合に便利です。

例えば、ユーザからの入力や多言語対応の検索において、大小文字の違いを気にせずにマッチさせたいときに役立ちます。

マルチライン/ドットに改行を含める指定(boost::regex_constants::mod_m/mod_s)

boost::regex_constants::mod_m(マルチラインモード)を指定すると、^$が文字列の行頭・行末にマッチするようになります。

これにより、複数行の文字列に対して行ごとの検索が可能です。

boost::regex regexPattern("^Hello", boost::regex_constants::mod_m);

また、boost::regex_constants::mod_s(ドットモード)を指定すると、.が改行文字も含めて任意の文字にマッチするようになります。

通常は.は改行にマッチしませんが、これを有効にすることで、より柔軟なパターン設計が可能です。

boost::regex regexPattern("a.b", boost::regex_constants::mod_s);

これらのオプションは、複雑なパターンや複数行にまたがる検索を行う際に役立ちます。

生文字列リテラルによる可読性向上

正規表現のパターン文字列は、エスケープシーケンスを多用するため、記述が煩雑になりやすいです。

特に、\を多用する正規表現では、\\と二重にエスケープする必要があり、可読性が低下します。

これを解決するために、C++11以降では生文字列リテラル(Raw String Literals)を利用できます。

生文字列リテラルは、R"(...)"の形式で記述し、エスケープシーケンスを無視して文字列を記述できるため、正規表現のパターンを見やすく書くことが可能です。

#include <boost/regex.hpp>
#include <string>
// 生文字列リテラルを使った正規表現パターン
boost::regex regexPattern(R"(\d{4}年\d{1,2}月\d{1,2}日)");

この例では、\d{}といった正規表現の特殊文字をエスケープせずに、そのまま記述できます。

これにより、パターンの可読性とメンテナンス性が向上します。

これらのポイントを押さえることで、Boost.Regexを用いた正規表現パターンの構築はより効率的かつ柔軟になります。

基本的な検索処理

Boost.Regexを用いた文字列検索の基本的な操作は、regex_searchregex_matchの2つです。

これらの関数は、それぞれ異なる目的に応じて使い分けられます。

ここでは、それぞれの関数の使い方と、返される結果の取り扱いについて詳しく解説します。

regex_searchによる部分一致取得

boost::regex_searchは、文字列の中から正規表現にマッチする部分を検索し、その部分を取得します。

文字列全体がパターンに一致しなくても、部分的に一致する箇所があれば検出可能です。

#include <iostream>
#include <boost/regex.hpp>
#include <string>
int main() {
    // 検索対象の文字列
    std::string text = "私の電話番号は090-1234-5678です。";
    // 電話番号のパターン
    boost::regex pattern(R"(\d{3}-\d{4}-\d{4})");
    // 検索結果を格納するオブジェクト
    boost::smatch matches;
    // 文字列内にパターンが存在するかを検索
    if (boost::regex_search(text, matches, pattern)) {
        // 最初にマッチした部分を出力
        std::cout << "見つかった電話番号: " << matches[0] << std::endl;
    } else {
        std::cout << "電話番号は見つかりませんでした。" << std::endl;
    }
    return 0;
}
見つかった電話番号: 090-1234-5678

この例では、regex_searchは文字列text内に電話番号のパターンが存在するかどうかを調べ、見つかった場合はmatches[0]にその部分を格納します。

matchesboost::smatch型で、operator[]を使ってキャプチャグループや全体のマッチ部分にアクセスできます。

regex_searchは、パターンにマッチした部分の位置や内容を取得したい場合に適しています。

複数のマッチを探す場合は、boost::regex_iteratorを併用します。

regex_matchによる完全一致確認

boost::regex_matchは、文字列全体が正規表現に一致するかどうかを判定します。

部分一致ではなく、文字列全体がパターンにマッチしている必要があります。

#include <iostream>
#include <boost/regex.hpp>
#include <string>
int main() {
    // 検索対象の文字列
    std::string text = "2025年04月20日";
    // 日付のパターン
    boost::regex pattern(R"(\d{4}年\d{1,2}月\d{1,2}日)");
    // 完全一致かどうかを判定
    if (boost::regex_match(text, pattern)) {
        std::cout << "文字列は日付形式に完全に一致します。" << std::endl;
    } else {
        std::cout << "文字列は日付形式に一致しません。" << std::endl;
    }
    return 0;
}
文字列は日付形式に完全に一致します。

この例では、regex_matchtext"2025年04月20日"という文字列全体とパターンに一致するかどうかを判定しています。

部分的に一致しているだけではなく、文字列全体がパターンに合致している必要があります。

regex_matchは、入力文字列が特定のフォーマットに完全に一致しているかを検証したい場合に便利です。

例えば、入力値のバリデーションやフォーマットの厳密な検査に適しています。

これらの関数を適切に使い分けることで、正規表現による文字列検索や検証を効率的に行うことが可能です。

マッチ情報の活用

正規表現による検索結果から得られる情報は、boost::smatchboost::match_resultsを用いて詳細にアクセスできます。

これらのクラスは、マッチした部分だけでなく、キャプチャグループや前後の文字列まで取得できるため、より柔軟な文字列処理が可能となります。

ここでは、それらの具体的な使い方と、名前付きキャプチャの利用方法について解説します。

boost::smatchでのサブマッチ取得

boost::smatchは、boost::regex_searchboost::regex_matchの結果を格納するためのクラスです。

これを使うことで、マッチした部分だけでなく、キャプチャグループに対応した部分も簡単に取り出せます。

#include <iostream>
#include <boost/regex.hpp>
#include <string>
int main() {
    // 検索対象の文字列
    std::string text = "私の誕生日は1990年12月31日です。";
    // 年月日をキャプチャするパターン
    boost::regex pattern(R"((\d{4})年(\d{1,2})月(\d{1,2})日)");
    // マッチ結果を格納
    boost::smatch matches;
    // 検索実行
    if (boost::regex_search(text, matches, pattern)) {
        // match[0]は全体のマッチ
        std::cout << "全体のマッチ: " << matches[0] << std::endl;
        // match[1]は最初のキャプチャグループ(年)
        std::cout << "年: " << matches[1] << std::endl;
        // match[2]は2番目のキャプチャグループ(月)
        std::cout << "月: " << matches[2] << std::endl;
        // match[3]は3番目のキャプチャグループ(日)
        std::cout << "日: " << matches[3] << std::endl;
    }
    return 0;
}
全体のマッチ: 1990年12月31日
年: 1990
月: 12
日: 31

この例では、matches[0]に全体のマッチ部分、matches[1]からmatches[3]にそれぞれのキャプチャグループの内容が格納されます。

operator[]を使って簡単にアクセスできるため、複雑なパターンから必要な情報だけを抽出するのに便利です。

prefix/suffixで前後文字列を取得

boost::smatchは、マッチした部分の前後の文字列も取得可能です。

prefix()suffix()メソッドを使うことで、マッチした部分の前後の文字列を簡単に取り出せます。

#include <boost/regex.hpp>
#include <iostream>
#include <string>

int main() {
    // 英語のASCII文字のみを使った文字列
    std::string text = "This text contains a certain pattern.";

    // ASCII文字のパターン。raw literalでエスケープ不要です。
    boost::regex pattern(R"(certain pattern)");
    boost::smatch matches;

    if (boost::regex_search(text, matches, pattern)) {
        // マッチした部分の前後を取得
        std::string before = matches.prefix().str();
        std::string matched = matches[0];
        std::string after = matches.suffix().str();

        std::cout << "Before: \"" << before << "\"" << std::endl;
        std::cout << "Matched: \"" << matched << "\"" << std::endl;
        std::cout << "After: \"" << after << "\"" << std::endl;
    } else {
        std::cout << "No match found." << std::endl;
    }

    return 0;
}
Before: "This text contains a "
Matched: "certain pattern"
After: "."

この例では、matches.prefix()はマッチした部分の前の文字列を返し、matches.suffix()は後の文字列を返します。

これにより、マッチした部分の前後のコンテキストを取得でき、より詳細な文字列操作が可能となります。

名前付きキャプチャの利用方法

正規表現のキャプチャグループに名前を付けることで、コードの可読性と保守性を向上させることができます。

Boost.Regexでは、(?<name>...)の形式で名前付きキャプチャを定義します。

#include <iostream>
#include <boost/regex.hpp>
#include <string>
int main() {
    std::string text = "メールアドレスはexample@test.comです。";
    // 名前付きキャプチャを使ったパターン
    boost::regex pattern(R"((?<user>[a-zA-Z0-9._%+-]+)@(?<domain>[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}))");
    boost::smatch matches;
    if (boost::regex_search(text, matches, pattern)) {
        // 名前付きキャプチャのアクセス
        std::string user = matches["user"];
        std::string domain = matches["domain"];
        std::cout << "ユーザ名: " << user << std::endl;
        std::cout << "ドメイン: " << domain << std::endl;
    }
    return 0;
}
ユーザ名: example
ドメイン: test.com

この例では、メールアドレスのユーザ名とドメイン部分にそれぞれ名前を付けてキャプチャしています。

matches["name"]の形式でアクセスできるため、キャプチャグループのインデックス番号を覚える必要がなく、コードの可読性が向上します。

これらの方法を駆使することで、正規表現のマッチ結果から必要な情報を効率的に抽出し、複雑な文字列処理をシンプルに行うことが可能です。

検索結果の置換

正規表現を用いた文字列操作の中で、検索結果を別の文字列に置き換える操作は非常に頻繁に行われます。

Boost.Regexでは、boost::regex_replace関数を使うことで、簡単に文字列の置換を実現できます。

また、置換の際に使用できるフォーマットの書式についても理解しておくと、より柔軟な置換処理が可能となります。

boost::regex_replaceによる単純置換

boost::regex_replaceは、指定した正規表現にマッチした部分を、任意の文字列に置き換える関数です。

基本的な使い方は、対象の文字列、正規表現パターン、置換後の文字列を引数に渡すだけです。

#include <iostream>
#include <boost/regex.hpp>
#include <string>
int main() {
    // 置換対象の文字列
    std::string text = "私の電話番号は090-1234-5678です。";
    // 電話番号のパターン
    boost::regex pattern(R"(\d{3}-\d{4}-\d{4})");
    // 置換後の文字列
    std::string replacement = "XXX-XXXX-XXXX";
    // 置換処理
    std::string result = boost::regex_replace(text, pattern, replacement);
    std::cout << "置換後の文字列: " << result << std::endl;
    return 0;
}

この例では、regex_replacetext内の電話番号部分を"XXX-XXXX-XXXX"に置き換えています。

置換後の文字列: 私の電話番号はXXX-XXXX-XXXXです。

boost::regex_replaceは、対象文字列のすべてのマッチ箇所を一括で置換します。

部分的な置換や条件付きの置換を行いたい場合は、正規表現のパターンや置換文字列を工夫します。

置換フォーマットの書式記述

置換文字列には、正規表現のキャプチャグループを参照するための特殊な書式を使用できます。

これにより、マッチした部分の一部だけを動的に置換したり、キャプチャした内容を再利用したりすることが可能です。

例えば、次の例では、日付のフォーマットを変換しています。

#include <iostream>
#include <boost/regex.hpp>
#include <string>
int main() {
    std::string text = "会議は2025年4月20日に予定されています。";
    // 日付のパターン
    boost::regex pattern(R"((\d{4})年(\d{1,2})月(\d{1,2})日)");
    // 置換フォーマット
    std::string format = "$1/$2/$3";
    // 置換処理
    std::string result = boost::regex_replace(text, pattern, format);
    std::cout << "変換後の文字列: " << result << std::endl;
    return 0;
}

この例では、$1$2$3はそれぞれキャプチャグループに対応し、置換後の文字列に挿入されます。

変換後の文字列: 会議は2025/4/20に予定されています。

この仕組みを使えば、部分的に抽出した情報を再利用したり、フォーマットを自在に変更したりできます。

また、複雑な置換を行いたい場合は、キャプチャグループの番号や名前を駆使して、柔軟な置換処理を実現できます。

Boost.Regexのregex_replaceは、多くの場面で文字列の整形やデータ変換に役立ちます。

パフォーマンスチューニング

正規表現を用いた文字列処理の効率性は、アプリケーションのパフォーマンスに大きく影響します。

特に、大量のデータや頻繁に実行される処理では、正規表現の最適化が重要です。

Boost.Regexでは、正規表現の事前コンパイルや最適化フラグを活用することで、処理速度を向上させることが可能です。

正規表現の事前コンパイルと再利用

正規表現のパターンを毎回コンパイルするのは、処理時間の無駄遣いとなります。

boost::regexオブジェクトは、一度作成すれば何度でも再利用できるため、これを活用します。

#include <iostream>
#include <boost/regex.hpp>
#include <string>
int main() {
    // 正規表現パターンを事前にコンパイル
    static const boost::regex pattern(R"(\d{4}-\d{2}-\d{2})");
    // 検索対象の文字列
    std::string text1 = "2025-04-20は祝日です。";
    std::string text2 = "会議は2025-05-01に予定されています。";
    // 何度も使う場合は、同じregexオブジェクトを再利用
    if (boost::regex_search(text1, pattern)) {
        std::cout << "テキスト1に日付が見つかりました。" << std::endl;
    }
    if (boost::regex_search(text2, pattern)) {
        std::cout << "テキスト2に日付が見つかりました。" << std::endl;
    }
    return 0;
}
テキスト1に日付が見つかりました。
テキスト2に日付が見つかりました。

この例では、patternを静的変数として定義し、複数の検索に使い回しています。

これにより、パターンのコンパイルコストを一度だけに抑え、処理速度を向上させることができます。

optimizeフラグの活用

boost::regexのコンストラクタにboost::regex_constants::optimizeフラグを指定すると、正規表現の最適化処理が行われ、検索速度が向上します。

ただし、最適化には時間がかかるため、パターンを頻繁に使い回す場合に効果的です。

#include <boost/regex.hpp>
#include <iostream>
#include <string>
int main() {
    // 正規表現の最適化フラグを指定
    boost::regex pattern(R"(\w+@\w+\.\w+)", boost::regex_constants::optimize);
    std::string email = "user@example.com";
    if (boost::regex_match(email, pattern)) {
        // メールアドレスの検証
        std::cout << "有効なメールアドレスです: " << email << std::endl;
    }
    return 0;
}
有効なメールアドレスです: user@example.com

この例では、optimizeフラグを付与することで、複数回のマッチングや検索処理の高速化を図っています。

特に、大規模なデータセットや頻繁に呼び出される関数内での正規表現処理において、パフォーマンスの向上が期待できます。

正規表現の事前コンパイルと最適化フラグの適切な活用は、アプリケーションのレスポンス向上や処理効率化に直結します。

エラーハンドリング

正規表現を使用する際には、パターンの誤りやその他の問題によって例外が発生する可能性があります。

Boost.Regexでは、boost::regex_error例外を通じてエラー情報を提供します。

これらの例外を適切に処理することで、プログラムの安定性と信頼性を向上させることが可能です。

boost::regex_error例外の種類

boost::regex_errorは、正規表現のコンパイルやマッチング処理中にエラーが発生した場合にスローされる例外です。

例外の種類は、boost::regex_constants::error_type列挙型で定義されており、エラーの内容を特定できます。

代表的なエラーの種類は以下の通りです。

エラータイプ内容説明
error_escapeエスケープシーケンスの誤りバックスラッシュの使い方が不正
error_brack角括弧の誤り角括弧内の構文エラー
error_paren丸括弧の誤りグループ化の括弧の不整合
error_brace波括弧の誤り波括弧の不正使用
error_badrepeat繰り返しの誤り量指定子の誤用や位置の問題
error_complexity複雑さの制限超過正規表現の複雑さが制限を超えた場合

これらのエラーは、正規表現のパターンをコンパイルしようとした際にboost::regexのコンストラクタやregex_matchregex_searchなどの関数からスローされます。

#include <iostream>
#include <boost/regex.hpp>
#include <string>
int main() {
    try {
        // 不正な正規表現パターン
        boost::regex pattern(R"([a-z)"); // 角括弧の閉じ忘れ
    } catch (const boost::regex_error& e) {
        std::cerr << "正規表現エラー: " << e.what() << std::endl;
        std::cerr << "エラータイプ: " << e.code() << std::endl;
        // e.code()はboost::regex_constants::error_typeを返す
    }
    return 0;
}
正規表現エラー: Found a closing ) with no corresponding opening parenthesis.  The error occurred while parsing the regular expression: '[a-z>>>HERE>>>'.
エラータイプ: 8

この例では、誤ったパターンによりboost::regex_error例外がスローされ、キャッチしてエラー内容を出力しています。

try-catchによる例外処理

正規表現の操作中に例外が発生する可能性を考慮し、try-catchブロックを用いて例外処理を行います。

これにより、エラー発生時にプログラムのクラッシュを防ぎ、適切なエラーメッセージを表示したり、リカバリー処理を行ったりできます。

#include <iostream>
#include <boost/regex.hpp>
#include <string>
int main() {
    try {
        std::string patternStr = R"(\d{4}-\d{2}-\d{2})"; // 正規表現パターン
        boost::regex pattern(patternStr, boost::regex_constants::optimize);
        std::string text = "2025-04-20";
        if (boost::regex_match(text, pattern)) {
            std::cout << "日付フォーマットに一致しました。" << std::endl;
        } else {
            std::cout << "一致しませんでした。" << std::endl;
        }
    } catch (const boost::regex_error& e) {
        std::cerr << "正規表現エラーが発生しました: " << e.what() << std::endl;
        // 必要に応じてエラーに応じた処理を追加
    }
    return 0;
}
日付フォーマットに一致しました。

この例では、正規表現のコンパイルやマッチング処理中に例外が発生した場合でも、catchブロックで捕捉し、エラーメッセージを出力しています。

正規表現のエラーは、パターンの誤りや構文ミスによって起こるため、事前にパターンの検証や例外処理を行うことが重要です。

これにより、アプリケーションの堅牢性を高め、ユーザに適切なフィードバックを提供できます。

Unicodeやローカルマルチバイト文字列の対応

多言語対応や国際化を考慮した正規表現処理では、Unicodeやローカルマルチバイト文字列に適切に対応する必要があります。

Boost.Regexは、std::wstringを用いたワイド文字列や、ロケール設定と組み合わせることで、多言語環境における正規表現の柔軟な運用を可能にします。

std::wstringを使ったwide文字列検索

std::wstringは、ワイド文字wchar_tを用いた文字列型であり、Unicode文字や多バイト文字を扱う際に有効です。

Boost.Regexも、boost::wregexを用いることで、ワイド文字列に対応した正規表現処理を行えます。

#include <iostream>
#include <boost/regex.hpp>
#include <string>
int main() {
    // ワイド文字列の例
    std::wstring text = L"こんにちは、世界!";
    // Unicode対応の正規表現パターン
    boost::wregex pattern(L"こんにちは");
    // 検索
    if (boost::regex_search(text, pattern)) {
        std::wcout << L"「こんにちは」が見つかりました。" << std::endl;
    } else {
        std::wcout << L"「こんにちは」が見つかりませんでした。" << std::endl;
    }
    return 0;
}

この例では、boost::wregexを使ってワイド文字列内の特定の文字列を検索しています。

std::wcoutを使うことで、Unicode文字も正しく出力できます。

また、正規表現パターンもワイド文字列リテラルL"..."を用いて記述します。

これにより、多言語の文字列や記号も正確に扱えます。

locale設定との組み合わせ

Unicodeやマルチバイト文字列を正しく処理するためには、ロケール設定も重要です。

std::localeを適切に設定することで、文字列の比較や変換、正規表現のマッチングにおいて、言語や地域に応じたルールを適用できます。

#include <iostream>
#include <locale>
#include <boost/regex.hpp>
#include <string>
int main() {
    // ロケールを設定
    std::locale::global(std::locale("ja_JP.UTF-8"));
    // ワイド文字列
    std::wstring text = L"こんにちは、世界!";
    // Unicode対応の正規表現
    boost::wregex pattern(L"こんにちは");
    // 検索
    if (boost::regex_search(text, pattern)) {
        std::wcout << L"ロケール設定と組み合わせて「こんにちは」が見つかりました。" << std::endl;
    } else {
        std::wcout << L"見つかりませんでした。" << std::endl;
    }
    return 0;
}

この例では、std::locale::globalを使って日本語UTF-8のロケールを設定しています。

これにより、正規表現のマッチングや文字列操作が、地域や言語のルールに従って行われるようになります。

ただし、ロケール設定は環境依存のため、実行環境に適したロケール名を指定する必要があります。

適切に設定することで、多言語対応のアプリケーションにおいても、正規表現の動作を安定させることが可能です。

これらの方法を駆使すれば、Unicodeやローカルマルチバイト文字列に対しても正規表現を効果的に適用でき、多言語対応のアプリケーション開発に役立ちます。

実践的な応用例

正規表現は、実際のアプリケーションやデータ処理において非常に有用です。

ここでは、メールアドレスの抽出、日付や郵便番号の検証、ログファイルからのIPアドレス抽出、CSVデータからの数値項目検出といった具体的な例を紹介します。

これらの例を通じて、Boost.Regexの実践的な使い方と応用範囲の広さを理解できます。

メールアドレス抽出

メールアドレスの抽出には、正規表現のキャプチャグループとパターンを工夫します。

以下の例では、テキスト中からメールアドレスをすべて抽出します。

#include <iostream>
#include <boost/regex.hpp>
#include <string>
#include <vector>
int main() {
    std::string text = "連絡先はabc@example.comとxyz@domain.co.jpです。";
    // メールアドレスの正規表現パターン
    boost::regex pattern(R"((?<user>[a-zA-Z0-9._%+-]+)@(?<domain>[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}))");
    auto begin = boost::sregex_iterator(text.begin(), text.end(), pattern);
    auto end = boost::sregex_iterator();
    std::cout << "抽出されたメールアドレス:\n";
    for (auto it = begin; it != end; ++it) {
        std::cout << it->str() << std::endl;
    }
    return 0;
}
抽出されたメールアドレス:
abc@example.com
xyz@domain.co.jp

この例では、boost::sregex_iteratorを使って、テキスト中のすべてのマッチを列挙しています。

(?<user>...)(?<domain>...)の名前付きキャプチャを利用して、メールアドレスのユーザ名とドメイン部分を抽出しています。

日付や郵便番号の検証

複数形式の日付パターン対応

異なる日付フォーマットに対応するために、複数のパターンを組み合わせるか、オプションを使ったパターンを作成します。

#include <iostream>
#include <boost/regex.hpp>
#include <string>
int main() {
    std::string text1 = "2025/04/20";
    std::string text2 = "2025-04-20";
    // 複数形式の日付パターン
    boost::regex pattern(R"((\d{4})[/-](\d{1,2})[/-](\d{1,2}))");
    auto check_date = [&](const std::string& date_str) {
        if (boost::regex_match(date_str, pattern)) {
            std::cout << date_str << " は有効な日付形式です。\n";
        } else {
            std::cout << date_str << " は無効な日付形式です。\n";
        }
    };
    check_date(text1);
    check_date(text2);
    return 0;
}
2025/04/20 は有効な日付形式です。
2025-04-20 は有効な日付形式です。

この例では、/または-で区切られた日付を検証しています。

複数の区切り文字に対応できるため、柔軟な検証が可能です。

条件付き検証

特定の条件を満たす日付や郵便番号だけを抽出したい場合は、正規表現に条件を組み込みます。

#include <iostream>
#include <boost/regex.hpp>
#include <string>
int main() {
    std::string text = "郵便番号は123-4567と890-1234です。";
    // 先頭が特定の数字から始まる郵便番号
    boost::regex pattern(R"((?:1|8)\d{2}-\d{4})");
    auto begin = boost::sregex_iterator(text.begin(), text.end(), pattern);
    auto end = boost::sregex_iterator();
    std::cout << "条件に合った郵便番号:\n";
    for (auto it = begin; it != end; ++it) {
        std::cout << it->str() << std::endl;
    }
    return 0;
}
条件に合った郵便番号:
123-4567
890-1234

この例では、郵便番号の先頭が1または8で始まるものだけを抽出しています。

ログファイルからIPアドレスを抽出

サーバーログやアクセスログからIPアドレスを抽出する例です。

#include <iostream>
#include <boost/regex.hpp>
#include <string>
#include <vector>
int main() {
    std::string log = R"(127.0.0.1 - - [20/Apr/2025:10:00:00 +0900] "GET /index.html HTTP/1.1" 200 1024
192.168.1.1 - - [20/Apr/2025:10:01:00 +0900] "POST /submit HTTP/1.1" 404 512)";
    // IPアドレスの正規表現
    boost::regex pattern(R"((\d{1,3}\.){3}\d{1,3})");
    auto begin = boost::sregex_iterator(log.begin(), log.end(), pattern);
    auto end = boost::sregex_iterator();
    std::cout << "抽出されたIPアドレス:\n";
    for (auto it = begin; it != end; ++it) {
        std::cout << it->str() << std::endl;
    }
    return 0;
}
抽出されたIPアドレス:
127.0.0.1
192.168.1.1

この例では、(\d{1,3}\.){3}\d{1,3}のパターンでIPアドレスを抽出しています。

CSVデータから数値項目を検出

CSVファイルの中から数値だけを抽出したい場合の例です。

#include <iostream>
#include <boost/regex.hpp>
#include <string>
int main() {
    std::string csv_line = "John,25,180.5,Engineer";
    // 数値の正規表現(整数と小数点含む)
    boost::regex pattern(R"(\b\d+(\.\d+)?\b)");
    auto begin = boost::sregex_iterator(csv_line.begin(), csv_line.end(), pattern);
    auto end = boost::sregex_iterator();
    std::cout << "CSV中の数値:\n";
    for (auto it = begin; it != end; ++it) {
        std::cout << it->str() << std::endl;
    }
    return 0;
}
CSV中の数値:
25
180.5

この例では、整数や小数点を含む数値を抽出しています。

\bは単語境界を示し、正確な数値の抽出に役立ちます。

これらの例を通じて、正規表現を用いたデータ抽出や検証の具体的な方法と、その応用範囲の広さを理解できます。

実際の開発やデータ分析において、これらの技術を積極的に活用してください。

テストとデバッグ

正規表現を用いた文字列処理の正確性と効率性を確保するためには、適切なテストとデバッグ手法が不可欠です。

ここでは、マッチ結果の可視化方法と、単体テストフレームワークを用いた検証方法について詳しく解説します。

マッチ結果の可視化

正規表現のマッチング結果を視覚的に確認することは、パターンの妥当性やデバッグに非常に役立ちます。

特に複雑な正規表現や、多数のキャプチャグループを含むパターンでは、結果の可視化が問題点の特定に直結します。

正規表現の可視化ツール

正規表現の構造やマッチングの流れを理解するために、専用の可視化ツールを利用することが推奨されます。

代表的なツールには以下があります。

  • RegExr:ブラウザ上で動作し、正規表現のパターンとテスト文字列を入力すると、マッチ箇所をハイライト表示します。キャプチャグループやフラグの設定も視覚的に確認でき、学習やデバッグに便利です
  • Regex101:詳細な解説や、正規表現の動作をリアルタイムで可視化します。特に、キャプチャグループの内容や、マッチした部分の位置を確認できるため、複雑なパターンの検証に適しています

これらのツールを使えば、正規表現の動作を直感的に理解でき、パターンの修正や最適化が容易になります。

regex101を使った迅速検証

regex101は、Webベースの正規表現テスターの中でも特に人気の高いツールです。

以下の手順で利用します。

  1. テストしたい正規表現パターンを入力。
  2. 検証したいテスト文字列を入力。
  3. マッチ箇所やキャプチャグループの内容をリアルタイムで確認。
  4. 必要に応じてフラグやエスケープシーケンスを調整。

例として、メールアドレス抽出のパターンをregex101に貼り付け、複数のメールアドレスが含まれるテキストで動作確認を行います。

これにより、パターンの妥当性やキャプチャの内容を即座に把握でき、デバッグ効率が向上します。

単体テストとの連携

正規表現の動作検証だけでなく、コード全体の品質向上には単体テストの導入が不可欠です。

Boost.Regexを用いた正規表現処理も、テストフレームワークと連携させることで、再現性のある検証と自動化が可能となります。

Boost.Testを使ったサンプル検証

BoostライブラリのBoost.Testは、C++の標準的なテストフレームワークです。

以下は、正規表現のマッチング結果を検証するサンプルです。

#define BOOST_TEST_MODULE RegexTest
#include <boost/test/included/unit_test.hpp>
#include <boost/regex.hpp>
BOOST_AUTO_TEST_CASE(test_email_extraction) {
    std::string text = "メールはtest@example.comです。";
    boost::regex pattern(R"((?<user>[a-zA-Z0-9._%+-]+)@(?<domain>[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}))");
    boost::smatch matches;
    BOOST_CHECK(boost::regex_search(text, matches, pattern));
    BOOST_CHECK_EQUAL(matches["user"], "test");
    BOOST_CHECK_EQUAL(matches["domain"], "example.com");
}

この例では、メールアドレスの抽出とキャプチャ内容の検証を行っています。

BOOST_CHECKBOOST_CHECK_EQUALを使うことで、期待値と実際の結果を比較し、テストの合否を自動判定します。

Google Testでのテスト実装

Google Testは、Googleが開発したC++のテストフレームワークです。

Boost.Regexと併用した例は以下の通りです。

#include <gtest/gtest.h>
#include <boost/regex.hpp>
TEST(RegexTest, EmailExtraction) {
    std::string text = "メールはsample@domain.comです。";
    boost::regex pattern(R"((?<user>[a-zA-Z0-9._%+-]+)@(?<domain>[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}))");
    boost::smatch matches;
    EXPECT_TRUE(boost::regex_search(text, matches, pattern));
    EXPECT_EQ(matches["user"], "sample");
    EXPECT_EQ(matches["domain"], "domain.com");
}

Google TestのEXPECT_TRUEEXPECT_EQを使えば、テストの自動化と結果の詳細な出力が可能です。

これらのテストとデバッグ手法を導入することで、正規表現のパターンや処理の正確性を担保し、長期的なメンテナンス性と信頼性を向上させることができます。

正規表現の複雑さに関わらず、体系的なテストと可視化を行うことが、品質向上の鍵となります。

まとめ

この記事では、Boost.Regexを使った正規表現の基本操作から応用例、パフォーマンス最適化、エラーハンドリング、Unicode対応、そして実践的なデータ抽出や検証方法まで詳しく解説しました。

これにより、効率的かつ安全に正規表現を活用できる知識と技術が身につきます。

関連記事

Back to top button
目次へ