2025年02月20日(木) [長年日記]
■ [c++] C++の正規表現メモ
<regex>の使い方をメモ。
正規表現オブジェクトとそれを使った文字列に対するマッチ処理について以下をメモする。
- std::basic_regex<CharT> (正規表現)
- std::regex_match() (全体マッチ判定)
- std::regex_search() (部分マッチ判定)
- std::regex_iterator<Iterator> (部分マッチ結果の列挙)
- std::regex_token_iterator<Iterator> (部分マッチ結果のサブマッチの列挙)
これら以外に文字列の置換機能もあるがそれについては割愛する。
std::basic_regex<CharT> (正規表現)
正規表現はstd::basic_regexクラステンプレートを使って表現する。
std::regex re{R"(abc (\d\d).(\d\d))"};
- std::regex は std::basic_regex<char> の別名であり、通常はこれを使うだろう。
- 「R"(...)"」部分は生文字リテラルであり、正規表現を書く際にはこの記法が役に立つ。
- 使える正規表現はデフォルトではECMAScript互換。コンストラクタで構文を切り替えることもできる。
- ひらがななどの非ASCII文字のサポートは期待できない模様(よくわかっていない)。
std::regex_match() (全体マッチ判定)
std::regex_match()を使うことで、文字列全体が正規表現にマッチするかどうかを判定できる。
void regex_match1() { static const std::regex re{R"(\w+ \d+/\d+/\d+)"}; const bool is_matched1 = std::regex_match("hello 2025/01/02", re); const bool is_matched2 = std::regex_match("hello world 2025/01/02", re); std::cout << "regex_match1" << std::endl; std::cout << is_matched1 << std::endl; //=> 1 std::cout << is_matched2 << std::endl; //=> 0 }
- 文字列の一部分ではなく全体がマッチする必要があることに注意。
regex_match()の引数にstd::match_resultsオブジェクトを指定すると、正規表現で指定したキャプチャグループ部分にマッチした内容を参照することができる。
void regex_match2() { static const std::regex re{R"(\w+ (\d+)/(\d+)/(\d+))"}; std::cmatch m; const bool is_matched = std::regex_match("hello 2025/01/02", m, re); std::cout << "regex_match2" << std::endl; std::cout << is_matched << std::endl; //=> 1 std::cout << m[0] << std::endl; //=> hello 2025/01/02 std::cout << m[1] << std::endl; //=> 2025 std::cout << m[2] << std::endl; //=> 01 std::cout << m[3] << std::endl; //=> 02 }
- std::cmatch は std::match_results<const char*> の別名。
- std::match_resultsクラステンプレートにはこのようなエイリアスがいくつか定義されている。が、std::string_viewを使う場合は std::match_results<std::string_view::const_iterator> などとしないといけないようだ。
- 上記の m[0] などで得られるのは単なる文字列ではなくstd::sub_matchオブジェクト。std::match_results は std::sub_match を要素に持つコンテナだと考えることができる。
std::regex_search() (部分マッチ判定)
std::regex_search()を使うことで、文字列が正規表現にマッチする部分を持つかどうかを判定できる。
void regex_search() { static const std::regex re{R"(\w+ (\d+)/(\d+)/(\d+))"}; std::cmatch m; const bool is_matched = std::regex_search("hello world 2025/01/02", m, re); std::cout << "regex_search" << std::endl; std::cout << is_matched << std::endl; //=> 1 std::cout << m[0] << std::endl; //=> world 2025/01/02 std::cout << m[1] << std::endl; //=> 2025 std::cout << m[2] << std::endl; //=> 01 std::cout << m[3] << std::endl; //=> 02 }
std::regex_iterator<Iterator> (部分マッチ結果の列挙)
std::regex_iteratorクラステンプレートを使うと、std::regex_search()を繰り返し呼び出し、それぞれの呼び出しに対するマッチ結果(std::match_results オブジェクト)を順番に得ることができる。
void regex_iterator() { static const std::regex re{R"(\w+ (\d+)/(\d+)/(\d+))"}; const std::string s = "hello world 2025/01/02 japan 1/2/3/4/5/6/7/8/9"; std::cout << "regex_iterator" << std::endl; for (std::sregex_iterator it{s.begin(), s.end(), re}, end; it != end; ++it) { std::cout << (*it)[0] << std::endl; std::cout << (*it)[1] << std::endl; std::cout << (*it)[2] << std::endl; std::cout << (*it)[3] << std::endl; std::cout << "---" << std::endl; } }
regex_iterator world 2025/01/02 2025 01 02 --- japan 1/2/3 1 2 3 ---
- sregex_iterator は regex_iterator<string::const_iterator> の別名。
std::regex_token_iterator<Iterator> (部分マッチ結果のサブマッチの列挙)
std::regex_token_iteratorクラステンプレートは regex_iterator に似ているが、マッチ結果ではなくサブマッチ(std::sub_match オブジェクト)の内容を直接得られるところが違う。
void regex_token_iterator() { static const std::regex re{R"(\w+ (\d+)/(\d+)/(\d+))"}; const std::string s = "hello world 2025/01/02 japan 1/2/3/4/5/6/7/8/9"; std::cout << "regex_token_iterator" << std::endl; for (std::sregex_token_iterator it{s.begin(), s.end(), re, {1, 3}}, end; it != end; ++it) { std::cout << *it << std::endl; } }
regex_token_iterator 2025 02 1 3
- sregex_token_iterator は regex_token_iterator<string::const_iterator> の別名。
- コンストラクタの4番目の引数で、何番目のサブマッチを列挙するかを指定する。ここに -1 を指定することでマッチしなかった部分を列挙することもできる。
[ツッコミを入れる]