C++教科書/標準ライブラリ編/<regex>の章

編集

はじめに

編集

テキスト処理では、特定のパターンに合致する部分文字列を検索・置換することがよくあります。この際、正規表現を使うと非常に柔軟で強力な処理が可能になります。C++11で導入された<regex>ヘッダーは、ECMAScript準拠の正規表現を提供し、パターンマッチングやテキスト変換などの機能を備えています。

正規表現クラスとその操作

編集

std::basic_regexクラステンプレート

編集

正規表現オブジェクトはstd::basic_regexクラステンプレートのインスタンスで表されます。

コンストラクタ
  • デフォルトコンストラクタ
  • 文字列から構築 (basic_regex(str, flags))
  • 反復子の範囲から構築 (basic_regex(first, last, flags))
代入演算子では、文字列や反復子の範囲から新しい正規表現を代入できます。
その他のメンバー関数
mark_count()
サブマッチの個数を返す
flags()
使用中のフラグを返す
swap()
別の正規表現オブジェクトと値を交換

std::regex_traits

編集

文字列処理のカスタマイズを行うトレイトスクラスです。特殊な文字の扱い方を指定したり、ロケールに基づいた照合順を利用できます。

例:

std::regex_traits<char> traits;
std::string name = traits.transform("ámígo", std::use_facet<std::ctype<char>>(loc));

マッチング関数

編集

std::regex_match

編集

与えられた文字列全体が正規表現にマッチするかをテストします。

std::string text = "Hello, World!";
std::regex re("Hello, World!");
std::smatch m;
bool matched = std::regex_match(text, m, re); // matched == true
編集

文字列のあらゆる部分が正規表現にマッチするかを探索します。

std::string text = "Hi Hello, World! Hello?";
std::regex re("Hello");
std::smatch m;
bool found = std::regex_search(text, m, re); // found == true

std::regex_replace

編集

正規表現にマッチする部分を置換した新しい文字列を生成します。

std::string text = "Hello, 2023! Hello, 2024?";
std::regex re("Hello, \\d+");
std::string replaced = std::regex_replace(text, re, "Hi");
// replaced == "Hi! Hi?"

マッチ結果を表すクラス

編集

std::sub_match

編集

サブマッチを表現するクラスで、マッチした部分文字列の開始・終了イテレータを保持します。length()メソッドでマッチした長さを取得でき、std::stringに変換して内容を参照できます。

std::match_results

編集

全体のマッチ結果を保持するコンテナクラスで、サブマッチごとにstd::sub_matchオブジェクトを格納しています。添字アクセスでサブマッチを取り出せます。さらに、prefix(), suffix()でマッチした前後の文字列を取得できます。

format()メンバー関数を使うと、マッチした部分だけを抜き出したり置換することができます。

std::string text = "My name is Jane. Her name is Jane too.";
std::regex re("(\\w+)\\s*is\\s*(\\w+)");
std::smatch m;
std::regex_search(text, m, re);
std::ssub_match sub1 = m[1]; // "My"
std::ssub_match sub2 = m[2]; // "Jane"

std::string fmt = "$2 was mentioned twice: '$&'";
std::string replaced = m.format(fmt); 
// replaced == "Jane was mentioned twice: 'My name is Jane. Her name is Jane too.'"

イテレータクラス

編集

std::regex_iterator

編集

文字列中に現れるすべての正規表現マッチを走査するイテレータです。エンドイテレータに到達するまでインクリメントを続けることで、順次マッチしたsub_matchオブジェクトを取得できます。

std::string text = "Hi! Hello, World! Hello, Regex!";
std::regex re("Hello[^!]+");
std::sregex_iterator iter(text.begin(), text.end(), re);
std::sregex_iterator end;
for (; iter != end; ++iter) {
    std::cout << iter->str() << '\n'; // Hello, World!, Hello, Regex!
}

std::regex_token_iterator

編集

正規表現にマッチした部分だけでなく、マッチしなかった部分(トークン)も取り出せるイテレータです。-1を指定するとマッチしなかった部分を、0以上を指定するとそのインデックスのサブマッチを取得できます。

std::string text = "Hi! Hello, World! Hello, Regex!";
std::regex re("Hello[^!]+");
std::sregex_token_iterator iter(text.begin(), text.end(), re, {-1, 0});
std::sregex_token_iterator end;
for (; iter != end; ++iter) {
    std::cout << *iter << '\n'; // Hi!, Hello, World, Hello, Regex, !
}

正規表現の構文とフラグ

編集

std::regex_constants

編集

<regex>ヘッダーでは、正規表現の構文やマッチのオプションなどが、std::regex_constantsの型エイリアスとして定義されています。

syntax_option_type
正規表現の構文ルールを指定するフラグ
match_flag_type
マッチングのオプション
error_type
エラーの種類を表す列挙型
std::regex re("HELLO", std::regex::icase); // 大文字小文字を区別しない
std::smatch m;
std::regex_search("hello", m, re); // マッチする

例題

編集

身分証番号の書式チェック:

std::regex id_regex("\\d{4}-\\d{4}-\\d{4}-\\d{2}");
std::string id = "1234-5678-9012-34";
if (std::regex_match(id, id_regex)) {
    std::cout << "Valid ID format\n";
}

URLからドメインを抽出:

std::regex url_regex("https?://([\\w.-]+)");
std::string url = "https://cpprefjp.github.io";
std::smatch m;
if (std::regex_search(url, m, url_regex)) {
    std::cout << "Domain: " << m[1] << '\n'; // cpprefjp.github.io
}

まとめ

編集

<regex>ヘッダーによって、C++プログラムに正規表現の機能が統合されました。正規表現は文字列処理において非常に強力なツールであり、パターンマッチングや検索、置換など様々な用途に役立ちます。

std::basic_regexクラスでは正規表現オブジェクトを作成し、std::regex_match、std::regex_search、std::regex_replaceといった関数で文字列操作を行えます。マッチした結果はstd::match_resultsに格納され、サブマッチへのアクセスやフォーマット出力が可能です。

さらに、std::regex_iteratorやstd::regex_token_iteratorを使えば、文字列中の複数の部分文字列を反復処理できます。また、std::regex_constantsで正規表現の構文やマッチオプションをカスタマイズできます。

正規表現の適切な利用により、プログラムのコード量を削減しながら、効率的で堅牢な文字列処理を実現できます。<regex>ヘッダーの提供する機能を理解し、上手く活用することが重要です。