SRELLの説明という形を採ってはいますが、特に断りがなければこの頁の内容はC++規格のstd::regex(C++11以降)にも当てはまります。
正規表現検索を行う際の大まかな流れは次の通りです。
正規表現オブジェクトの作成・保持を行うクラスです。使いやすいように次のような型がtypedefされています。
typedef basic_regex<char> regex; typedef basic_regex<wchar_t> wregex; // ここから下はSRELL独自のもの。 typedef basic_regex<char, u8regex_traits<char> > u8cregex; // C++11対応コンパイラ用。 typedef basic_regex<char16_t, u16regex_traits<char16_t> > u16regex; typedef basic_regex<char32_t> u32regex; // wchar_tの大きさにより、wregex を u32wregex か u16wregex かにtypedefする。 #if defined(WCHAR_MAX) #if WCHAR_MAX >= 0x10ffff typedef wregex u32wregex; typedef u32wregex u1632wregex; #elif WCHAR_MAX >= 0xffff typedef basic_regex<wchar_t, u16regex_traits<wchar_t> > u16wregex; typedef u16wregex u1632wregex; #endif #endif // C++20対応コンパイラ用。 typedef basic_regex<char8_t, u8regex_traits<char8_t> > u8regex; // C++20非対応コンパイラ用。上とは排他的に定義される。 typedef u8cregex u8regex;
C++規格の<regex>にあるものはregex, wregexの2つだけで、u(8c?|16w?|32w?|1632w)というprefixが付いているものはSRELLの拡張です。
u16wregexとu32wregexとはWCHAR_MAXの大きさによって排他的にtypedefされます。そのためソースコードのポータビリティーに問題が発生しうるということに後になって気づいたため、SRELL 2.930でu1632wregexを導入しました。ただこれも今にして思えば単にuwregexとすべきだったかもしれません。
basic_regexクラスで正規表現オブジェクトを作成するには、
という3つの方法があり、それぞれに「引数として文字列のポインタまたはbasic_string型インスタンスの参照を渡し、新規にコンパイルする方法」「basic_regex型インスタンスの参照を渡してコピーする方法」「同moveする方法(C++11以降)」「initializer_listを渡す方法(C++11以降)」があります。
basic_regex(const basic_regex& e); // コピーコンストラクタ。 basic_regex(basic_regex&& e) noexcept; // C++11以降。 explicit basic_regex(const charT* p, flag_type f = regex_constants::ECMAScript); basic_regex(const charT* p, size_t len, flag_type f = regex_constants::ECMAScript); template <class ST, class SA> explicit basic_regex(const std::basic_string<charT, ST, SA>& p, flag_type f = regex_constants::ECMAScript); template <class ForwardIterator> basic_regex(ForwardIterator first, ForwardIterator last, flag_type f = regex_constants::ECMAScript); basic_regex(initializer_list<charT> il, const flag_type f = regex_constants::ECMAScript); // C++11以降。
basic_regex& operator=(const basic_regex&); // 代入演算子。 basic_regex& operator=(basic_regex&&) noexcept; // C++11以降。 basic_regex& operator=(const charT* ptr); template <class ST, class SA> basic_regex& operator=(const std::basic_string<charT, ST, SA>& p); basic_regex& operator=(initializer_list<charT> il); // C++11以降。
basic_regex& assign(const basic_regex&); basic_regex& assign(regex_basic&&) noexcept; // C++11以降。 basic_regex& assign(const charT* ptr, flag_type f = regex_constants::ECMAScript); basic_regex& assign(const charT* p, size_t len, flag_type f = regex_constants::ECMAScript); template <class ST, class A> basic_regex& assign(const std::basic_string<charT, ST, A>& s, flag_type f = regex_constants::ECMAScript); template <class InputIterator> basic_regex& assign(InputIterator first, InputIterator last, flag_type f = regex_constants::ECMAScript); basic_regex& assign(initializer_list<charT> il, const flag_type f = regex_constants::ECMAScript); // C++11以降。
flag_typeという型は、regex_constants::syntax_option_typeをbasic_regex内でtypedefしたものです。その名が示す通りコンパイル時の文法解釈を指定するためのフラグで、次のような値が定義されています。
namespace regex_constants { static const syntax_option_type icase; // 大文字小文字を同一視する。ECMAScriptやPerlの //i に相当。 static const syntax_option_type ECMAScript; // 文字列をECMAScript (JavaScript) の正規表現として解釈する (既定値)。 static const syntax_option_type multiline; // ^や$が行頭・行末にもマッチするようにする。 // ECMAScriptやPerlの //m に相当。 static const syntax_option_type dotall; // SRELL 2.000以降の独自拡張。'.' があらゆる文字にマッチするようにする。 // ECMAScript 2018以降のRegExpやPerlの //s に相当。 static const syntax_option_type unicodesets; // SRELL 4.000以降の独自拡張。vモードを使用するようにする。 // ECMAScriptのRegExpの //v に相当。 }
これらは | (or) で区切ることによって同時に指定することも出来ます。
std::regexではこの他にもnosubs, optimize, collate, basic, extended, awk, grep, egrepなどの値が定義されていますが、SRELLでは上記のもの以外は認識されません(定義そのものはされていますので指定してもコンパイルエラーにはなりません)。
これらはbasic_regexクラス内でflag_type型の値としても定義されていますので、"regex_constants::icase" は "regex::icase" のように書くことも出来ます(regex::のところは実際に使う型に合わせます)。
basic_regexクラスのメンバのうち、主なものは次の通りです。
unsigned mark_count() const; // 正規表現内部に存在する「文字列を捕獲する括弧」の数を返す。 flag_type flags() const; // 正規表現コンパイル時に指定したflagを返す。 locale_type imbue(locale_type loc); // オブジェクトのlocaleを指定する。返値は変更前のlocale。 // locale_type は、traits::locale_type をtypedefしたもの。 locale_type getloc() const; // オブジェクトのlocaleを返す。 void swap(basic_regex& e); // 正規表現オブジェクトの交換をする。 std::size_t limit_counter; // SRELL拡張。詳細は「長考対策」を参照。
localeはicase検索で使うtolowerや、\d, \s, \wなどで使うis系函数の挙動を変えるためのものですが、SRELLはECMAScript準拠であることからlocale周りの実装がすべて省略されています。imbueを行っても何も変わらず、getlocが返すのは常に現在の大域localeです(std::locale()を返します)。
basic_regexに関しては、クラスメンバ以外にも次のようなものがあります。
template <class charT, class traits> void swap(basic_regex<charT, traits>& lhs, basic_regex<charT, traits>& rhs);
さらにSRELL 4.009以降では独自拡張として、match(), search(), replace(), split()が追加されています。これらの説明はトップ頁に記してあります。
正規表現をコンパイルしている最中にエラーが発生すると、regex_errorクラスがthrowされます。regex_errorクラスはstd::runtime_errorクラスを継承したもので、メンバはruntime_errorから継承したwhat()の他、エラー番号を読み出す regex_constants::error_type code() という函数のみです。
srell::basic_regexがthrowするエラー一覧は次の通りです。
namespace regex_constants
{
static const error_type error_escape; // 解釈不可能な\、または\で終わっている。
static const error_type error_backref; // \n番に該当する括弧がない。
static const error_type error_brack; // []が非対称。
static const error_type error_paren; // ()が非対称。
static const error_type error_brace; // {}が非対称。
static const error_type error_badbrace; // {3,2}など範囲指定がおかしい。
static const error_type error_range; // [b-a]など範囲指定がおかしい。
static const error_type error_badrepeat; // *?+{の前に変なものがある。
// 以下はSRELL拡張。
static const error_type error_lookbehind; // SRELL 1のみ。戻り読みが固定幅に非ず。
static const error_type error_utf8; // SRELL 2.630以降。不正なUTF-8が正規表現中に混入。
static const error_type error_property; // SRELL 3.010以降。未知のUnicode property。
static const error_type error_noescape; // SRELL 4.000以降、vモードのみ。[]内で( ) [ ] { } / - \ |
は要エスケープ。
static const error_type error_operator; // SRELL 4.000以降、vモードのみ。文字クラスの演算エラー。
static const error_type error_complement; // SRELL 4.000以降、vモードのみ。文字列の補集合を使おうとした。
static const error_type error_modifier; // SRELL 4.007以降。埋込フラグを先頭以外で使った。
// または一つの括弧内で同じフラグを複数回指定した。
}
このようにbasic_regexは、正規表現におかしなところがあるとthrowすることでその旨を伝えてきます。そのため特にユーザに正規表現を入力させるようなアプリケーションの場合、try~catchは必須です。
Algorithmにはregex_match(), regex_search(), regex_replace()の3種類があります。
文字列の中から正規表現に合致する部分を探し出し、見つかったらtrue、そうでないならfalseを返します。
引数には、
をこの順番で渡します。
文字列の渡し方には、「[begin, end)のiteratorペアを渡す」「文字列のポインタを渡す」「basic_string型インスタンスの参照を渡す」の3種類があり、それぞれに「引数としてmatch_results型インスタンスの参照を渡す・渡さない」の選択肢があるため、3×2で計6種類の書式が存在します。
さらにSRELL 2.600以降では、「[begin, end)のiteratorペアと、戻り読み (lookbehind) を処理する際の逆行限界を指定するiteratorとを渡す」という選択肢も追加されています。
template <class BidirectionalIterator, class Allocator, class charT, class traits> bool regex_search( BidirectionalIterator first, BidirectionalIterator last, match_results<BidirectionalIterator, Allocator>& m, const basic_regex<charT, traits>& e, regex_constants::match_flag_type flags = regex_constants::match_default); template <class BidirectionalIterator, class charT, class traits> bool regex_search( BidirectionalIterator first, BidirectionalIterator last, const basic_regex<charT, traits>& e, regex_constants::match_flag_type flags = regex_constants::match_default); // マッチするかどうかだけ知りたい時は、match_resultsは渡さずとも良い。 template <class charT, class Allocator, class traits> bool regex_search( const charT* str, match_results<const charT*, Allocator>& m, const basic_regex<charT, traits>& e, regex_constants::match_flag_type flags = regex_constants::match_default); template <class charT, class traits> bool regex_search( const charT* str, const basic_regex<charT, traits>& e, regex_constants::match_flag_type flags = regex_constants::match_default); // 上からmatch_resultsを省略したもの。 template <class ST, class SA, class Allocator, class charT, class traits> bool regex_search( const std::basic_string<charT, ST, SA>& s, match_results<typename std::basic_string<charT, ST, SA>::const_iterator, Allocator>& m, const basic_regex<charT, traits>& e, regex_constants::match_flag_type flags = regex_constants::match_default); template <class ST, class SA, class charT, class traits> bool regex_search( const std::basic_string<charT, ST, SA>& s, const basic_regex<charT, traits>& e, regex_constants::match_flag_type flags = regex_constants::match_default); // 上からmatch_resultsを省略したもの。 // 以下はSRELL 2の独自拡張。2.600以降で利用可能。 bool regex_search( BidirectionalIterator first, BidirectionalIterator last, BidirectionalIterator lookbehind_limit, match_results<BidirectionalIterator, Allocator>& m, const basic_regex<charT, traits>& e, regex_constants::match_flag_type flags = regex_constants::match_default); template <class BidirectionalIterator, class charT, class traits> bool regex_search( BidirectionalIterator first, BidirectionalIterator last, BidirectionalIterator lookbehind_limit, const basic_regex<charT, traits>& e, regex_constants::match_flag_type flags = regex_constants::match_default); // 上からmatch_resultsを省略したもの。
引数としてmatch_results型インスタンスmの参照を渡しておいた場合、正規表現に合致する部分が文字列中に見つかった時には、マッチした位置の情報をmにセットしたうえでtrueを返してきます。
検索オプションを指定するregex_constants::match_flag_type型には、次のようなフラグオプション値が定義されています。これらは | (or) で区切ることによって同時に複数指定することも出来ます。
namespace regex_constants { typedef bitmask_type match_flag_type; static const match_flag_type match_default = 0; static const match_flag_type match_not_bol; // 引数として渡した文字列の先頭に^がマッチしないようにする。 static const match_flag_type match_not_eol; // 引数として渡した文字列の末尾に$がマッチしないようにする。 static const match_flag_type match_not_bow; // 引数として渡した文字列の先頭を単語の始まりと見なさせない。\bに影響を及ぼす。 static const match_flag_type match_not_eow; // 引数として渡した文字列の末尾を単語の終わりと見なさせない。\bに影響を及ぼす。 static const match_flag_type match_not_null; // 空文字列にマッチさせない。 static const match_flag_type match_continuous; // 引数として渡した文字列の先頭からマッチするかどうかのみ調べる。 // ECMAScript (JavaScript) のRegExpの //y フラグ (sticky) 相当。 static const match_flag_type match_prev_avail; // 引数として渡した文字列先頭の一文字前 (--first) は有効な位置。 // このフラグがセットされると、上のmatch_not_bol, match_not_bowは無視される。 static const match_flag_type match_lblim_avail; // SRELL 2の独自拡張。match_results.lookbehind_limit を戻り読み (lookbehind) 可能な範囲の限界と見なす。 // また ^ はアルゴリズム函数に渡したfirstではなく、match_results.lookbehind_limitのほうに // マッチするようになる。SRELL 2.300~2.500にのみ存在するフラグ。2.600で廃止。 }
この他にもC++の仕様書には match_any というフラグが存在しますが、SRELLでは機能しません(定義自体はされていますので指定してもコンパイルエラーにはなりません)。
アルゴリズムがthrowするエラー一覧は次の通りです。
namespace regex_constants { static const error_type error_complexity; // 複雑すぎる正規表現。 static const error_type error_stack; // メモリ不足。 }
正規表現が対象文字列全体とぴったり合致するならtrue、そうでないならfalseを返します。それ以外はregex_search()と同じです。
書式も次の通り、regex_searchのそれと同じです。
template <class BidirectionalIterator, class Allocator, class charT, class traits> bool regex_match( BidirectionalIterator first, BidirectionalIterator last, match_results<BidirectionalIterator, Allocator>& m, const basic_regex<charT, traits>& e, regex_constants::match_flag_type flags = regex_constants::match_default); template <class BidirectionalIterator, class charT, class traits> bool regex_match( BidirectionalIterator first, BidirectionalIterator last, const basic_regex<charT, traits>& e, regex_constants::match_flag_type flags = regex_constants::match_default); template <class charT, class Allocator, class traits> bool regex_match( const charT* str, match_results<const charT*, Allocator>& m, const basic_regex<charT, traits>& e, regex_constants::match_flag_type flags = regex_constants::match_default); template <class charT, class traits> bool regex_match( const charT* str, const basic_regex<charT, traits>& e, regex_constants::match_flag_type flags = regex_constants::match_default) template <class ST, class SA, class Allocator, class charT, class traits> bool regex_match( const std::basic_string<charT, ST, SA>& s, match_results<typename std::basic_string<charT, ST, SA>::const_iterator, Allocator>& m, const basic_regex<charT, traits>& e, regex_constants::match_flag_type flags = regex_constants::match_default); template <class ST, class SA, class charT, class traits> bool regex_match( const std::basic_string<charT, ST, SA>& s, const basic_regex<charT, traits>& e, regex_constants::match_flag_type flags = regex_constants::match_default);
1. 検索対象文字列、2. 正規表現オブジェクト、3. 書式文字列の3つを渡すと、検索対象文字列の中から正規表現に合致する箇所を探し出し、その結果を書式文字列の中へ流し込んで返してくる函数です。いわゆる「置換」と考えても良いのですが、函数の呼出後も検索対象文字列は元の状態のまま残り、置換後の文字列はreturnされてくるという点で純粋な置換処理とは異なります。
検索対象文字列の渡し方には例によって「[begin, end)のiteratorペアを渡す」「文字列のポインタを渡す」「basic_string型インスタンスの参照を渡す」の3種類があります。このうち「iteratorペアを渡す」という方法を選んだ場合、結果は第1引数で指定したOutputIteratorに対して出力されます。それ以外の2種類を選んだ場合、結果はbasic_string型のインスタンスに詰め込まれてreturnされてきます。
これら3種類にそれぞれ「書式を表す文字列をconst basic_string<charT>&型で渡すか、const charT*型で渡すか」という選択肢があるため、引数の渡し方には3×2で計6通りが存在します。
template <class OutputIterator, class BidirectionalIterator, class traits, class charT, class ST, class SA> OutputIterator regex_replace( OutputIterator out, BidirectionalIterator first, BidirectionalIterator last, const basic_regex<charT, traits>& e, const std::basic_string<charT, ST, SA>& fmt, regex_constants::match_flag_type flags = regex_constants::match_default); // [first, last)は検索対象文字列、eは正規表現オブジェクト。 // fmtの書式に従い、マッチの結果をoutに出力する。 template <class OutputIterator, class BidirectionalIterator, class traits, class charT> OutputIterator regex_replace( OutputIterator out, BidirectionalIterator first, BidirectionalIterator last, const basic_regex<charT, traits>& e, const charT* fmt, const regex_constants::match_flag_type flags = regex_constants::match_default); // fmtがcharT*型になっていることを除けば上に同じ。 template <class traits, class charT, class ST, class SA, class FST, class FSA> std::basic_string<charT, ST, SA> regex_replace( const std::basic_string<charT, ST, SA>& s, const basic_regex<charT, traits>& e, const std::basic_string<charT, FST, FSA>& fmt, regex_constants::match_flag_type flags = regex_constants::match_default); // sは検索対象文字列、eは正規表現オブジェクト。 // 検索結果をfmtの書式に従って整形したものをreturnする。 template <class traits, class charT, class ST, class SA> std::basic_string<charT, ST, SA> regex_replace( const std::basic_string<charT, ST, SA>& s, const basic_regex<charT, traits>& e, const charT* fmt, const regex_constants::match_flag_type flags = regex_constants::match_default) // fmtがcharT*型になっていることを除けば上に同じ。 template <class traits, class charT, class ST, class SA> std::basic_string<charT> regex_replace( const charT* s, const basic_regex<charT, traits>& e, const std::basic_string<charT, ST, SA>& fmt, const regex_constants::match_flag_type flags = regex_constants::match_default); // sは検索対象文字列へのポインタ、eは正規表現オブジェクト。 // 検索結果をfmtの書式に従って整形したものをreturnする。 template <class traits, class charT> std::basic_string<charT> regex_replace( const charT* s, const basic_regex<charT, traits>& e, const charT* fmt, const regex_constants::match_flag_type flags = regex_constants::match_default); // fmtがcharT*型になっていることを除けば上に同じ。
使用例を示します。
int main() { srell::regex exp("\\d(\\d)(\\d)"); // 正規表現。 std::string str("123-456-789"); // 検索対象。 std::string fmt("マッチした部分 = $&\n1番目の括弧 = $1\n2番目の括弧 = $2\n\n"); // 書式。 std::ostreambuf_iterator<char> os(std::cout); srell::regex_replace(os, str.begin(), str.end(), exp, fmt, srell::regex_constants::format_no_copy); // 上は次のように書いても同じ結果がもたらされる。 // std::cout << srell::regex_replace(str, exp, fmt, srell::regex_constants::format_no_copy) << std::endl; return 0; } 実行結果: マッチした部分 = 123 1番目の括弧 = 2 2番目の括弧 = 3 マッチした部分 = 456 1番目の括弧 = 5 2番目の括弧 = 6 マッチした部分 = 789 1番目の括弧 = 8 2番目の括弧 = 9
fmt中の$&, $1, $2等が検索結果によって置き換えられています。このような置き換えの対象となるのは、$&, $`, $', $1, $2, ... などECMAScript Language Specificationの15.5.4.11で定義されているシンボルです。なお書式を表す文字列中で $ 自身を表すには $$ と書きます。
regex_replaceの最後の引数には、regex_search()やregex_match()で使われるマッチフラグオプションに加えて次のものが指定できます。これらも | (or) で区切ることによって同時に指定することが出来ます。
static const match_flag_type format_default = 0; static const match_flag_type format_no_copy; // マッチしていない部分(&`や&'に当たる箇所)の文字列は // 自動的に出力しない。 static const match_flag_type format_first_only; // 最初にマッチした部分のみ出力する。
regex_replace()は、ECMAScriptやPerlの正規表現でいうところのgオプションが既定でオンになっていて、検索対象文字列中のマッチする箇所すべてが置き換えの対象となります。先ほどの例で、regex_replace()を一回呼び出しただけにもかかわらず正規表現に該当する3箇所 ("123", "456", "789") すべてが出力されてきたのもそのためです。
これをオフにして最初にマッチする箇所だけを対象としたい場合にはformat_first_onlyを指定します。
また既定の振る舞いでは、最初にマッチした箇所より前方の文字列、最後にマッチした箇所より後方の文字列、マッチした箇所同士の間の文字列もそのまま出力されてきます。これをオフにしてマッチした箇所だけを対象としたい時にはformat_no_copyを指定します。
format_- first_only |
format_- no_copy |
←文字列先頭 文字列末尾→ | ||||
---|---|---|---|---|---|---|
間 | マッチ1箇所目 | 間 | マッチ2箇所目 | 間 | ||
無指定 | 無指定 | そのまま 出力 |
マッチした文字列で fmt中の$&, $1などを 置き換えて出力 |
そのまま 出力 |
マッチした文字列で fmt中の$&, $1などを 置き換えて出力 |
そのまま 出力 |
指定あり | 出力なし | 出力なし | 出力なし | |||
指定あり | 無指定 | そのまま 出力 |
そのまま 出力 |
|||
指定あり | 出力なし | 出力なし |
少し分かりづらいかと思われますので具体例を挙げます。
// regex_search()でPerlの s/.../.../ や s/.../.../g 風の処理を行う例。 int main() { std::string str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz"; // 対象文字列。 const srell::regex re("\\d{2}"); const std::string fmt("<>"); // 書式文字列。 // $str =~ s/\d{2}/<>/ 風の処理(最初の1つだけ置換)。 std::string res1 = srell::regex_replace(str, re, fmt , srell::regex_constants::format_first_only); // $str =~ s/\d{2}/<>/g 風の処理(該当箇所はすべて置換)。 std::string res2 = srell::regex_replace(str, re, fmt); // printfを使うなら。 std::printf("result1: %s\n", res1.c_str()); std::printf("result2: %s\n", res2.c_str()); // iostreamを使うなら。 std::cout << "result1: " << res1 << std::endl; std::cout << "result2: " << res2 << std::endl; // ここで str.swap(res1) または str.swap(res2) とすることで「置換」になる。 return 0; } 実行結果: result1: ABCDEFGHIJKLMNOPQRSTUVWXYZ<>23456789abcdefghijklmnopqrstuvwxyz result2: ABCDEFGHIJKLMNOPQRSTUVWXYZ<><><><><>abcdefghijklmnopqrstuvwxyz
この例において、正規表現reが検索対象文字列strに対して最初にマッチするのは "01" の部分です。その段階でregex_replace()はまずformat_no_copyが指定されているかどうかを調べ、指定されていなければマッチした箇所(この場合 "01")より前の文字列("ABC...XYZ")を出力します。
そして次に、マッチした箇所を書式文字列 (fmt) の指定に従って置き換えます。ところがこの例では書式文字列fmtの中に "$&" が含まれていませんので、正規表現にマッチした箇所は出力対象に含まれません。そのため、結果として正規表現にマッチした箇所が書式文字列 "<>" そのものに置き換えられてしまうことになります。
これが終わるとregex_replace()はformat_first_onlyが指定されているかどうかを調べます。そして指定されていれば再度format_no_copyが指定されているか調べて、指定されていればそこで処理を終え、指定されていなければマッチした箇所より後の文字列("234...xyz")をそのまま出力してから処理を終えます。こうして出力されたのが、result1の文字列です。
format_first_onlyが指定されていなかった場合、regex_replace()は今回マッチした箇所の次("1" と "2" の間)を起点にして、再度正規表現reにマッチする箇所があるかどうかを調べます。そしてさらに見つかった場合、format_no_copyが指定されていなければ、起点("01"の次)から今回見つかった箇所("23")の間の文字列を出力します。
この繰り返しによって "01", "23", "45", "67", "89" の部分が "<>" によって置き換えられたものが出力されてゆき、結果、result2の文字列が出来上がります。
先述の通り、いずれの場合も結果はOutputIteratorへ出力またはstd::basic_string型のインスタンスで返されてきます。Perlのように元の文字列が置き換わるのではないことにご注意ください。前記サンプルコード中のコメントにもありますように、置換に相当する処理を行うには最後にswap()または代入をする必要があります。
上記のものの他、C++の仕様書には format_sed というオプションフラグが定義されていますが、SRELLでは機能しません(指定自体は可能)。
アルゴリズムは以上ですべてです。
正規表現検索の結果が収められるクラスです。match_resultsからは次のような型がtypedefされています。
typedef match_results<const char*> cmatch; typedef match_results<const wchar_t*> wcmatch; typedef match_results<std::string::const_iterator> smatch; typedef match_results<std::wstring::const_iterator> wsmatch; // ここから下はSRELL独自のもの。 typedef cmatch u8ccmatch; typedef smatch u8csmatch; // C++11対応コンパイラ用。 typedef match_results<const char16_t*> u16cmatch; typedef match_results<const char32_t*> u32cmatch; typedef match_results<std::u16string::const_iterator> u16smatch; typedef match_results<std::u32string::const_iterator> u32smatch; // wchar_tの大きさにより、w[cs]match を u32w[cs]match か u16w[cs]match かにtypedefする。 #if defined(WCHAR_MAX) #if WCHAR_MAX >= 0x10ffff typedef wcmatch u32wcmatch; typedef wsmatch u32wsmatch; typedef u32wcmatch u1632wcmatch; typedef u32wsmatch u1632wsmatch; #elif WCHAR_MAX >= 0xffff typedef wcmatch u16wcmatch; typedef wsmatch u16wsmatch; typedef u16wcmatch u1632wcmatch; typedef u16wsmatch u1632wsmatch; #endif #endif // C++20対応コンパイラ用。 typedef match_results<const char8_t*> u8cmatch; typedef match_results<std::u8string::const_iterator> u8smatch; // C++20非対応コンパイラ用。上2つとは排他的に定義される。 typedef u8ccmatch u8cmatch; typedef u8csmatch u8smatch;
cmatch系とsmatch系とがあって少しややこしいのですが、Cのconstポインタ型なのがcmatch型、basic_stringのconst_iterator型なのがsmatch型です。これらはregex_match()やregex_search()に対して渡す検索対象文字列の型と一致させる必要があります。
match_resultsクラスは検索の結果を受け取るためのものということもあり、ほとんどのメンバが読み出し専用です。
bool ready() const; // regex_match()やregex_search()の結果をセットされたことがあるならtrueを返す。 size_type size() const; // マッチした場合は正規表現中の // 「文字列を捕獲する(後方参照可能な)括弧」の数+1を、 // マッチしなかった場合は0を返す。 bool empty() const; // return size() == 0;
作成された直後のmatch_results型のインスタンスmは、メンバ函数ready()がfalseを返してきます。しかしひとたびregex_match()やregex_search()の結果を受けとると、マッチングの成否に関係なくm.ready()がtrueを返してくるようになります。
m.size()の「文字列を捕獲する括弧」というのは /.+(\d+).+(abc|def)/ のような正規表現における (\d+) や (abc|def) のことです。括弧でも後から参照できない(?:)のようなものは数に入りません。
その数が+1されるのは、検索後、正規表現全体にマッチした部分も「暗黙の0番括弧によって捕獲された」と見なす仕組みになっているためです。
regex_match()やregex_search()は引数にmatch_results型インスタンスmへの参照が渡されてくると、マッチングに成功した際mに次の情報をセットします。
これらのデータはすべてmatch_results::value_type型のインスタンスとしてmatch_results内部に保持されています。ちなみにこの型は後述するsub_match<BidirectionalIterator>型のtypedefです。
typedef sub_match<BidirectionalIterator> value_type; typedef const value_type& const_reference; // 以下略。
一方マッチしなかった時には、m.ready() == true, m.size() == 0, m.empty() == true となること以外は未定義とされています。
マッチした部分の位置情報を取り出すために、次のメンバ函数が用意されています。
const_reference operator[](size_type n) const; // sub番括弧のデータを保持するvalue_type型インスタンスへのconst参照。 // [0]が正規表現全体にマッチした位置、[1]以降が括弧の位置情報を保持。 const_reference prefix() const; // 正規表現がマッチした部分より前の部分のデータを保持するvalue_type型 // インスタンスへのconst参照。 const_reference suffix() const; // 正規表現がマッチした部分より後の部分のデータを保持するvalue_type型 // インスタンスへのconst参照。 difference_type position(size_type sub = 0) const; // sub番括弧に捕獲された文字列の先頭位置を返す。起点は文字列全体の先頭。 difference_type length(size_type sub = 0) const; // sub番括弧に捕獲された文字列の要素数を返す。 string_type str(size_type sub = 0) const; // sub番括弧が捕獲した文字列を返す。 const_iterator begin() const; const_iterator cbegin() const; // 0番括弧~n番括弧までのデータを保持する vector<value_type> の // 先頭const_iteratorを返す。 const_iterator end() const; const_iterator cend() const; // 同最終const_iteratorの次を返す。つまり上のと合わせて[begin, end)。
さらにSRELL 2では、名前付きキャプチャに対応するために次のようなメンバが独自に追加定義されています。引数として括弧の番号ではなく後方参照名を表す文字列を取ること以外は、前掲した同名のメンバ函数と同じ振る舞いをします。
// SRELL 2の独自拡張。 const_reference operator[](const string_type &sub) const; difference_type position(const string_type &sub) const; difference_type length(const string_type &sub) const; string_type str(const string_type &sub) const;
SRELL 2.300~2.500では、アルゴリズム函数に渡す検索対象範囲とは別に、戻り読み(lookbehind)が行われる際の「逆行できる限界」をセットすることが出来るように次のメンバ函数が追加されています。
// SRELL 2の独自拡張。 BidirectionalIterator lookbehind_limit;
このlookbehind_limit
メンバは、アルゴリズムにmatch_lblim_availフラグを渡した時のみ有効になります。
SRELL 2.600以降では、regex_search()に直接逆行限界を渡せるようになりました。そのためlookbehind_limit
メンバは廃止されています。
その他、主なメンバ函数には次のようなものがあります。
template <class OutputIter> OutputIter format( OutputIter out, const string_type& fmt, regex_constants::match_flag_type flags = regex_constants::format_default) const; // fmtの書式に従って、現在保持している検索結果をOutputIterに出力する。 string_type format( const string_type& fmt, regex_constants::match_flag_type flags = regex_constants::format_default) const; // string_type型のインスタンスをreturnするという点以外、上と同じ。 void swap(match_results&); // match_resultsインスタンスの中身の交換。
メンバ函数format()の引数fmtというのは、algorithmのregex_replace()で出てきたfmtと同じものです。実のところregex_replace()は、このformat()を繰り返し呼び出しているだけです。
match_resultsクラスに関してはクラスメンバ以外にもswap()やoperator==(), operator!=()などが定義されています。
template <class BidirectionalIterator, class Allocator> void swap( match_results<BidirectionalIterator, Allocator>& m1, match_results<BidirectionalIterator, Allocator>& m2); template <class BidirectionalIterator, class Allocator> bool operator==( const match_results<BidirectionalIterator, Allocator>& m1, const match_results<BidirectionalIterator, Allocator>& m2); template <class BidirectionalIterator, class Allocator> bool operator!=( const match_results<BidirectionalIterator, Allocator>& m1, const match_results<BidirectionalIterator, Allocator>& m2);
match_resultsクラス内部で、正規表現にマッチした部分の位置情報を保持・管理するのに使われているのがこのクラスです。ライブラリを使う側がこの型のインスタンスを直接宣言する機会はまずないでしょう。match_resultsのメンバへの参照を行うのに使う場合がほとんどかと思われます。
sub_matchからは次のような型がtypedefされています。
typedef sub_match<const char*> csub_match; typedef sub_match<const wchar_t*> wcsub_match; typedef sub_match<std::string::const_iterator> ssub_match; typedef sub_match<std::wstring::const_iterator> wssub_match; // ここから下はSRELL独自のもの。 typedef csub_match u8ccsub_match; typedef ssub_match u8cssub_match; // C++11対応コンパイラ用。 typedef sub_match_results<const char16_t*> u16csub_match; typedef sub_match_results<const char32_t*> u32csub_match; typedef sub_match_results<std::u16string::const_iterator> u16ssub_match; typedef sub_match_results<std::u32string::const_iterator> u32ssub_match; // wchar_tの大きさにより、w[cs]submatch を u32w[cs]submatch か u16w[cs]submatch かにtypedefする。 #if defined(WCHAR_MAX) #if WCHAR_MAX >= 0x10ffff typedef wcsub_match u32wcsub_match; typedef wssub_match u32wssub_match; typedef u32wcsub_match u1632wcsub_match; typedef u32wssub_match u1632wssub_match; #elif WCHAR_MAX >= 0xffff typedef wcsub_match u16wcsub_match; typedef wssub_match u16wssub_match; typedef u16wcsub_match u1632wcsub_match; typedef u16wssub_match u1632wssub_match; #endif #endif // C++20対応コンパイラ用。 typedef sub_match_results<const char8_t*> u8csub_match; typedef sub_match_results<std::u8string::const_iterator> u8ssub_match; // C++20非対応コンパイラ用。上2つとは排他的に定義される。 typedef u8ccsub_match u8csub_match; typedef u8cssub_match u8ssub_match;
match_resultsの場合と同じく、csub_match系とssub_match系とがあります。もっとも、match_results内部で使われているsub_match型は、value_type型やconst_reference型にtypedefされていますので、そちらを使ったほうが型の間違いが起こりにくいでしょう(新autoが使えるC++11以降ならこれすら使わないかもしれません)。
sub_matchクラスの主なメンバは次の通りです。
BidirectionalIterator first; // 文字列の先頭位置を指す。 BidirectionalIterator second; // 文字列の末尾+1を指す。上のと合わせて[first, second)。 // この2つは継承元であるstd::pairのメンバ。 bool matched; // このインスタンスが何かを捕獲しているかどうかのbool。 // falseの時はfirst, secondの値とも無効。 typedef typename std::iterator_traits<BidirectionalIterator>::value_type value_type; typedef typename std::iterator_traits<BidirectionalIterator>::difference_type difference_type; difference_type length() const; // first~second間の要素数を返す。 operator std::basic_string<value_type>() const; // キャスト演算子。 std::basic_string<value_type> str() const; // 上2つはfirst~second間の文字列のコピーを返す。 int compare(const sub_match& s) const; // str().compare(s.str())の結果を返す。 int compare(const std::basic_string<value_type>& s) const; int compare(const value_type* s) const; // 上2つはstr().compare(s)の結果を返す。
基底クラスのstd::pairから継承したfirstとsecondとが、マッチした位置ないし()で捕獲した文字列の位置を[first, second)の形で表します。
matchedは少し注意が必要です。match_results型のインスタンスをmとした時、m[0]やm[1]などm.operator[]()のmatchedと、m.prefix(), m.suffix()のmatchedとでは意味するものが異なります。
operator[]()におけるmatchedは、「空文字であれとにかく捕獲できたかどうか」を意味します。例えば "abc" =~ /a(.?)b|c(.?)d/ のような場合、m[1].matchedはtrueですが、m[2].matchedはfalseになります。
一方、m.prefix()やm.suffix()のmatchedは、「自身の長さ(first~second間の長さ)が1文字以上あるかどうか」を意味します。
sub_matchクラスに関してはクラスメンバ以外にも次のようなものがあります。
// cout << m[0] << endl; のような表記用。 template <class charT, class ST, class BiIter> std::basic_ostream<charT, ST>& operator<<( std::basic_ostream<charT, ST>& os, const sub_match<BiIter>& m); // この他、sub_match型インスタンス同士や、sub_match型インスタンスと // 文字列のポインタ・basic_string型インスタンスとの比較を行う // operator函数群。数が多いので省略。
コンストラクタに正規表現オブジェクトと検索対象文字列とを渡すと、regex_iterator型のインスタンスiterはすぐさま内部でregex_search()を呼び出し、最初にマッチした位置の情報を、iter内部のmatch_results型インスタンスへ書き込みます。外部からこのインスタンスへは*iterないしiter->でアクセスします。
iterは++されると、次のマッチングポイントを探すべく再度regex_search()を呼び出します。その結果マッチする箇所が見つかったなら先ほどと同じことを繰り返します。
一方マッチする箇所がなかった場合、iterは "end-of-sequence iterator" という特別な状態のiteratorへと移行します。
regex_iteratorはforward iteratorに分類されますので、--iterは出来ません(補註:2022年になって実はinput iteratorであることが判明しました)。
regex_iteratorからは次のような型がtypedefされています。通常はこのうちのいずれかを使用します。
typedef regex_iterator<const char *> cregex_iterator; typedef regex_iterator<const wchar_t *> wcregex_iterator; typedef regex_iterator<std::string::const_iterator> sregex_iterator; typedef regex_iterator<std::wstring::const_iterator> wsregex_iterator; // ここから下はSRELL独自のもの。 typedef regex_iterator<const char *, typename std::iterator_traits<const char *>::value_type, u8regex_traits<typename std::iterator_traits<const char *>::value_type> > u8ccregex_iterator; typedef regex_iterator<std::string::const_iterator, typename std::iterator_traits<std::string::const_iterator>::value_type, u8regex_traits<typename std::iterator_traits<std::string::const_iterator>::value_type> > u8csregex_iterator; // C++11対応コンパイラ用。 #if defined(SRELL_CPP11_CHAR1632_ENABLED) typedef regex_iterator<const char16_t *> u16cregex_iterator; typedef regex_iterator<const char32_t *> u32cregex_iterator; typedef regex_iterator<std::u16string::const_iterator> u16sregex_iterator; typedef regex_iterator<std::u32string::const_iterator> u32sregex_iterator; #endif #if defined(WCHAR_MAX) #if WCHAR_MAX >= 0x10ffff typedef wcregex_iterator u32wcregex_iterator; typedef wsregex_iterator u32wsregex_iterator; typedef u32wcregex_iterator u1632wcregex_iterator; typedef u32wsregex_iterator u1632wsregex_iterator; #elif WCHAR_MAX >= 0xffff typedef regex_iterator<const wchar_t *, typename std::iterator_traits<const wchar_t *>::value_type, u16regex_traits<typename std::iterator_traits<const wchar_t *>::value_type> > u16wcregex_iterator; typedef regex_iterator<std::wstring::const_iterator, typename std::iterator_traits<std::wstring::const_iterator>::value_type, u16regex_traits<typename std::iterator_traits<std::wstring::const_iterator>::value_type> > u16wsregex_iterator; typedef u16wcregex_iterator u1632wcregex_iterator; typedef u16wsregex_iterator u1632wsregex_iterator; #endif #endif // C++20対応コンパイラ用。 typedef regex_iterator<const char8_t *> u8cregex_iterator; typedef regex_iterator<std::u8string::const_iterator> u8sregex_iterator; // C++20非対応コンパイラ用。上2つとは排他的に定義される。 typedef u8ccregex_iterator u8cregex_iterator; typedef u8csregex_iterator u8sregex_iterator;
regex_iteratorクラスのメンバは次の通りです。
typedef basic_regex<charT, traits> regex_type; typedef match_results<BidirectionalIterator> value_type; typedef std::ptrdiff_t difference_type; typedef const value_type* pointer; typedef const value_type& reference; typedef std::forward_iterator_tag iterator_category; regex_iterator(); regex_iterator(const BidirectionalIterator a, const BidirectionalIterator b, const regex_type& re, const regex_constants::match_flag_type m = regex_constants::match_default); regex_iterator(const regex_iterator&); regex_iterator& operator=(const regex_iterator&); bool operator==(const regex_iterator& right) const; bool operator!=(const regex_iterator& right) const; const value_type& operator*() const; const value_type* operator->() const; regex_iterator& operator++(); regex_iterator operator++(int);
コンストラクタには2種類あります。引数がないほうのコンストラクタで作られたインスタンスは、最初からend-of-sequence iteratorになります。
もう一つのほうは、検索対象文字列の最初を第1引数として、最後+1を第2引数として受け取り(つまり[begin, end))、第3引数以下で正規表現オブジェクトとマッチフラグとを受け取ります。こちらのコンストラクタによって作られたインスタンスが、前述のマッチする箇所を渡り歩くiteratorとして機能します。
このiteratorは検索対象文字列内にマッチする部分があるうちは、end-of-sequence iteratorとの比較がfalseになりますが、もうマッチする箇所がなくなるとtrueになります。
// regex_iteratorの使用例。 srell::sregex_iterator eos; // End-of-Sequence. srell::sregex_iterator iter(begin, end, re, flags); for (; iter != eos; ++iter) { // *iterやiter->でmatch_results型インスタンスにアクセス。 }
ちなみにSRELLのアーカイヴに同梱されているunicode/ucfdataout.cppでは、実際にこのregex_iteratorを利用して行の切り出しを行っています。
さらに余談ですが、<regex>の元となったBoost.Regexのドキュメントによれば、このクラスは元々regex_grepというものだったそうです。
C++の仕様によると、regex_iteratorが受け取ったbasic_regex型インスタンスはiterator内部でコピーされず、単にポインタを保持する仕組みになっています。そのため次のようなことをすると、iteratorを使っているうちにアクセス違反で落ちてしまいます(経験談)。
srell::sregex_iterator iter(begin, end, srell::regex("^.*$"));
regex_iteratorはmatch_results型のインスタンスを指すiteratorであるのに対して、こちらのregex_token_iteratorは、match_results型インスタンス内のsub_match型インスタンスを指すiteratorです。
regex_token_iteratorからは次のような型がtypedefされています。通常はこのうちのいずれかを使用します。
typedef regex_token_iterator<const char *> cregex_token_iterator; typedef regex_token_iterator<const wchar_t *> wcregex_token_iterator; typedef regex_token_iterator<std::string::const_iterator> sregex_token_iterator; typedef regex_token_iterator<std::wstring::const_iterator> wsregex_token_iterator; // ここから下はSRELL独自のもの。 typedef regex_token_iterator<const char *, typename std::iterator_traits<const char *>::value_type, u8regex_traits<typename std::iterator_traits<const char *>::value_type> > u8ccregex_token_iterator; typedef regex_token_iterator<std::string::const_iterator, typename std::iterator_traits<std::string::const_iterator>::value_type, u8regex_traits<typename std::iterator_traits<std::string::const_iterator>::value_type> > u8csregex_token_iterator; // C++11対応コンパイラ用。 #if defined(SRELL_CPP11_CHAR1632_ENABLED) typedef regex_token_iterator<const char16_t *> u16cregex_token_iterator; typedef regex_token_iterator<const char32_t *> u32cregex_token_iterator; typedef regex_token_iterator<std::u16string::const_iterator> u16sregex_token_iterator; typedef regex_token_iterator<std::u32string::const_iterator> u32sregex_token_iterator; #endif #if defined(WCHAR_MAX) #if WCHAR_MAX >= 0x10ffff typedef wcregex_token_iterator u32wcregex_token_iterator; typedef wsregex_token_iterator u32wsregex_token_iterator; typedef u32wcregex_token_iterator u1632wcregex_token_iterator; typedef u32wsregex_token_iterator u1632wsregex_token_iterator; #elif WCHAR_MAX >= 0xffff typedef regex_token_iterator<const wchar_t *, typename std::iterator_traits<const wchar_t *>::value_type, u16regex_traits<typename std::iterator_traits<const wchar_t *>::value_type> > u16wcregex_token_iterator; typedef regex_token_iterator<std::wstring::const_iterator, typename std::iterator_traits<std::wstring::const_iterator>::value_type, u16regex_traits<typename std::iterator_traits<std::wstring::const_iterator>::value_type> > u16wsregex_token_iterator; typedef u16wcregex_token_iterator u1632wcregex_token_iterator; typedef u16wsregex_token_iterator u1632wsregex_token_iterator; #endif #endif // C++20対応コンパイラ用。 typedef regex_token_iterator<const char8_t *> u8cregex_token_iterator; typedef regex_token_iterator<std::u8string::const_iterator> u8sregex_token_iterator; // C++20非対応コンパイラ用。上2つとは排他的に定義される。 typedef u8ccregex_token_iterator u8cregex_token_iterator; typedef u8csregex_token_iterator u8sregex_token_iterator;
regex_token_iteratorクラスのメンバは次の通りです。
typedef basic_regex<charT, traits> regex_type; typedef sub_match<BidirectionalIterator> value_type; typedef std::ptrdiff_t difference_type; typedef const value_type* pointer; typedef const value_type& reference; typedef std::forward_iterator_tag iterator_category; regex_token_iterator(); regex_token_iterator(BidirectionalIterator a, BidirectionalIterator b, const regex_type& re, int submatch = 0, regex_constants::match_flag_type m = regex_constants::match_default ); regex_token_iterator(BidirectionalIterator a, BidirectionalIterator b, const regex_type& re, const std::vector<int>& submatches, regex_constants::match_flag_type m = regex_constants::match_default ); // For C++11. regex_token_iterator(BidirectionalIterator a, BidirectionalIterator b, const regex_type& re, std::initializer_list<int> submatches, regex_constants::match_flag_type m = regex_constants::match_default ); template <std::size_t N> regex_token_iterator(BidirectionalIterator a, BidirectionalIterator b, const regex_type& re, const int (&submatches)[N], regex_constants::match_flag_type m = regex_constants::match_default ); regex_token_iterator(const regex_token_iterator&); regex_token_iterator& operator=(const regex_token_iterator&); bool operator==(const regex_token_iterator&); bool operator!=(const regex_token_iterator&); const value_type& operator*(); const value_type* operator->(); regex_token_iterator& operator++(); regex_token_iterator operator++(int);
引数のないコンストラクタでend-of-sequence iteratorを作り、それとの比較が != である間は有効という仕組みもregex_iteratorの場合と同じです。
唯一違うのは、引数ありのほうのコンストラクタが第4引数としてint型の配列を受け取ることです。例えばこの配列に { 2, 0, 1 } という順番で数値が収められていたとすると、作成された直後のregex_token_iterator型インスタンスiterは、最初にマッチした箇所のm[2] (mはiterが内部に保持するmatch_results型のインスタンス)を指し、以後++iterするたびにポイント先がm[0], m[1]へと、配列で指定された順番通りに渡り歩いてゆきます。そこからさらに++iterすると、今度は次にマッチした箇所のm[2]を指します。
また配列の中に-1があると、その順番の時には「前回マッチした箇所の次から今回マッチした箇所の手前まで(m.prefix()に相当)」を指すようになります。さらに最後にマッチした箇所の最後のsub_matchを指した後、end-of-sequenceへと移行する直前に、「最後にマッチした箇所の次から文字列の終わりまで(m.suffix()に相当)」を指す段階が追加されます。
対象文字列 | 間 | マッチ1箇所目 | 間 | マッチ2箇所目 | 間 | ||||
---|---|---|---|---|---|---|---|---|---|
match_results &m = *iter; |
1回目の m.prefix() |
m[0] | m[1] | m[2] | 2回目の m.prefix() |
m[0] | m[1] | m[2] | 2回目の m.suffix() |
順番 | - | 1 | 2 | 0 | - | 4 | 5 | 3 | - |
対象文字列 | 間 | マッチ1箇所目 | 間 | マッチ2箇所目 | 間 | ||||
---|---|---|---|---|---|---|---|---|---|
match_results &m = *iter; |
1回目の m.prefix() |
m[0] | m[1] | m[2] | 2回目の m.prefix() |
m[0] | m[1] | m[2] | 2回目の m.suffix() |
順番 | 2 | 1 | 3 | 0 | 6 | 5 | 7 | 4 | 8 |
-1指定は正規表現によって文字列を分割(いわゆるsplit)する用途を想定したものと思われます。一例を示します。
// 正規表現によるsplit処理の例。 // "abcd01efgh23ijklmn45opqrst67uvwx89yz".split(/\d+/) に相当することを行う。 int main() { const std::string str = "abcd01efgh23ijklmn45opqrst67uvwx89yz"; const srell::regex re("\\d+"); const int submatches[] = { -1 }; // マッチする箇所以外を出力する。 srell::sregex_token_iterator it(str.begin(), str.end(), re, submatches), eoi; for (; it != eoi; ++it) { // printfを使うなら。 const std::string res(it->first, it->second); std::printf("result: %s\n", res.c_str()); // iostreamを使うなら。 std::cout << "result: " << *it << std::endl; } return 0; } 実行結果: result: abcd result: efgh result: ijklmn result: opqrst result: uvwx result: yz
<regex>の元となったBoost.Regexのドキュメントによれば、このクラスはregex_splitを派生させたもののようです。