NIREはC++用の正規表現テンプレートライブラリです。
NIREの開発は終了しました。新機能の追加や性能の向上についてはもう行わず、必要に応じてバグ修正のみ行う予定です。
NIREはSRELLをフォークした正規表現ライブラリです。次のような特徴をSRELLから継承しています。
'.'
がUTF-16文字列でサロゲートペアの片割れだけにマッチしたり、UTF-8文字列のコードユニットにマッチしたりするようなことがありません。[丈𠀋]
のように指定できます。また [\u{1b000}-\u{1b0ff}]
のような範囲指定もできます。
一方SRELLから継承しなかったものは、1) <regex>互換のインターフェイス、2) 任意のイテレータへの対応などです。
NIREのインターフェイスについては、ECMAScript (JavaScript) の仕様を参考にしています。
動作確認済みコンパイラのうちもっとも古いものはVC++2005です。
nire.hpp をincludeするだけです。
// Example 01: #include <cstdio> #include <string> #include <iostream> #include "nire.hpp" int main() { nire::RegExp re; // 正規表現オブジェクト。 nire::Match m; // 結果を収めるオブジェクト。 re = "\\d+[^-\\d]+"; // 正規表現をコンパイル。 m = re.exec("1234-5678-90ab-cdef"); // 検索する。 if (m) { std::printf("result: %s\n", m[0].str().c_str()); // printfを使うなら。 std::cout << "result: " << m[0] << std::endl; // iostreamを使うなら。 } return 0; }
Zipアーカイヴ内の reame_ja.txt も併せてご覧ください。
NIREは次の4クラスから成り立ちます。すべてnamespace nireの下に置かれています。
regexp<charT>
クラス:正規表現オブジェクト。このメンバ関数を用いて検索や置換を行います。re_match<charT>
クラス:検索結果を保持します。re_group<charT>
構造体:マッチした箇所やキャプチャ文字列の範囲などの位置情報を保持します。re_error
クラス:エラー発生時に上記クラスからthrow
されてくるものです。
上3つはテンプレートとして実装されていて、charT
の部分には検索に使用する文字型を指定します。
あらかじめ次のような型がtypedef
されていますので、通常はこのいずれかを使います。
型 | regexp | re_match | re_group |
文字列の解釈 | 備考 |
---|---|---|---|---|---|
char |
u8cRegExp | Match | Group |
UTF-8 | |
wchar_t |
u16wRegExp | wMatch | wGroup |
UTF-16 | WCHAR_MAX が0xFFFF 以上、0x10FFFF 未満の場合のみ。 |
u32wRegExp |
UTF-32 | WCHAR_MAX が0x10FFFF 以上の場合のみ。 |
|||
char8_t |
u8RegExp | u8Match | u8Group |
UTF-8 | |
char16_t |
u16RegExp | u16Match | u16Group |
UTF-16 | |
char32_t |
u32RegExp | u32Match | u32Group |
UTF-32 | |
char |
bRegExp | Match | Group |
0 からUCHAR_MAX までの値の連続。 |
最後のbRegExp
というのはバイナリ配列のようなものに対して検索を行うためのものです。詳しくは後述します。
正規表現オブジェクトの作成、保持ならびに検索や置換を行います。NIREの中核をなすクラスで、ECMAScript (JavaScript) のRegExp
クラスの仕様を参考にしています。
次のメンバ変数ならびにメンバ函数が定義されています。
インスタンスの作成と同時に正規表現オブジェクトの新規作成も行う場合、正規表現文字列の渡し方としては「1) ヌル終端文字列へのポインタを渡す」「2) ポインタと文字列の長さとを渡す」「3) std::basic_string
へのconst
参照を渡す」の3通りがあります。
それぞれフラグの渡し方に「a) nire::flag::type
型の値を渡す」「b) フラグ文字列へのポインタを渡す」の2通りがありますので、3×2=6通りの渡し方が存在するということになります。
regexp(); regexp(const regexp &r); // 既存のものをコピー。 regexp(regexp &&r) noexcept; // C++11以降。 // ポインタ regexp(const charT *const p, const flag::type f = flag::ECMAScript); regexp(const charT *const p, const charT *const flags); // ポインタと文字列長 regexp(const charT *const p, const std::size_t len, const flag::type f); regexp(const charT *const p, const std::size_t len, const charT *const flags); // basic_string regexp(const std::basic_string<charT> &str, const flag::type f = flag::ECMAScript); regexp(const std::basic_string<charT> &str, const charT *const flags);
フラグには次のものがあります。
nire::flag::type
型の値として渡す時は |
(or) で区切ることにより複数のフラグを同時に渡すことが出来ます。
文字列として渡す時は、"im"
のように書くことにより複数のフラグを渡すことが出来ます。
nire::flag::type 型の値として渡す時 |
文字列として渡す時 | |
---|---|---|
nire::flag::global |
"g" |
グローバル検索。マッチした箇所の直後の位置をlastIndex メンバに記録し、次はそこを起点に検索する。 |
nire::flag::icase |
"i" |
大文字小文字の区別がない検索。 |
nire::flag::multiline |
"m" |
^ と$ とが改行文字の前後にもマッチするようにする。 |
nire::flag::dotall |
"s" |
. が改行文字も含むあらゆる文字にマッチするようにする。 |
nire::flag::sticky |
"y" |
文字列のlastIndex 番目の要素からマッチするかどうかのみを調べる。 |
nire::flag::noflags |
"" |
フラグ無しであることをあえて指定したい場合。 |
既存の正規表現オブジェクトのコピー、移動(C++11以降のみ)または新規作成を行います。
新規作成の場合、正規表現文字列およびフラグの渡し方にはコンストラクタ同様6通りあります。
regexp &assign(const regexp &r); regexp &assign(regexp &&r) noexcept; // C++11以降。 // ポインタ regexp &assign(const charT *const p, const flag::type f = flag::ECMAScript); regexp &assign(const charT *const p, const charT *const flags); // ポインタと文字列長 regexp &assign(const charT *const p, std::size_t len, const flag::type f); regexp &assign(const charT *const p, std::size_t len, const charT *const flags); // basic_string regexp &assign(const std::basic_string<charT> &str, const flag::type f = flag::ECMAScript); regexp &assign(const std::basic_string<charT> &str, const charT *const flags);
既存の正規表現オブジェクトのコピー、移動(C++11以降のみ)または新規作成を行います。
コンストラクタやassign()
とは異なり、引数を1つしか指定できませんので、新規作成の場合は「1) ヌル終端文字列へのポインタを渡す」「2) std::basic_string
へのconst
参照を渡す」の2択です。
regexp &operator=(const regexp &r); regexp &operator=(regexp &&r) noexcept; // C++11以降。 // ポインタ regexp &operator=(const charT *const p) // basic_string regexp &operator=(const std::basic_string<charT> &str)
パターンコンパイル時にglobal
フラグかsticky
フラグかが指定されていた場合、対象文字列のlastIndex
番目の要素から検索が行われるようになります。どちらのフラグもセットされていない場合はlastIndex
の値は無視され、常に対象文字列の先頭から検索が行われます。
検索して正規表現にマッチする位置が見つかった場合、その範囲の次([begin, end) で言うところのend)のオフセット値がlastIndex
に書き込まれます。見つからなかった場合は0
にリセットされます。
検索対象文字列を変えた場合でもlastIndex
は自動的にクリアされません。
このメンバはmutable
指定されていますので、regexp
のインスタンスそのものがconst
指定されていても書き込むことが出来ます。
検索を行うメンバ函数です。
検索対象文字列の渡し方には「1) ヌル終端文字列へのポインタを渡す」「2) ポインタと文字列の長さとを渡す」「3) std::basic_string
へのconst
参照を渡す」の3通りがあります。
結果の受け取り方には「a) re_match
型インスタンスの参照を引数として渡しておき、検索結果はそこに書き込んでもらう」「b) 検索結果を書き込んだre_match
型インスタンスを戻り値としてreturn
してもらう」の2通りがあります。
re_match
型インスタンスの参照を引数として渡した場合、戻り値は「マッチしたかどうかを表すbool
値」です。
// ポインタ bool exec( re_match<charT> &m, const charT *const str ) const; re_match<charT> exec( const charT *const str ) const; // ポインタと文字列長 bool exec( re_match<charT> &m, const charT *const str, const std::size_t len ) const; re_match<charT> exec( const charT *const str, const std::size_t len ) const; // basic_string bool exec( re_match<charT> &m, const std::basic_string<charT> &str ) const; re_match<charT> exec( const std::basic_string<charT> &str ) const;
global
フラグかsticky
フラグかが指定されていた場合、検索はlastIndex
で指定されたオフセット位置より始まります。そしてマッチする箇所があった時にはそのマッチした範囲 [begin, end) のendのオフセット位置がlastIndex
に書き込まれ、なかった時にはlastIndex
には0
が書き込まれます。
繰り返しの中で使う場合はre_match
型インスタンスを参照で渡す版の使用をお勧めします。
検索対象文字列の中にマッチする箇所があるかないかだけを調べます。それ以外はexec()
と同じです。
// ポインタ bool test( const charT *const str ) const; // ポインタと文字列長 bool test( const charT *const str, const std::size_t len ) const; // basic_string bool test( const std::basic_string<charT> &str ) const;
JavaScriptのString.prototype.replace(RegExpオブジェクト, 置換文字列またはコールバック函数)
に準拠した動作をします。
検索対象文字列中の「正規表現とマッチする箇所」を別の文字列に置き換えます。置き換えられた文字列は戻り値としてreturn
されてきて、元の検索対象文字列はそのまま残ります。
置換後の文字列の指定方法としては「1) 書式文字列を渡す」「2) コールバック函数を渡す」の2通りがあります。
global
フラグがセットされていた時は、対象文字列中の正規表現にマッチする箇所すべてが置換の対象となります。それ以外の時は最初にマッチした部分だけが対象となります。
// 書式文字列による置換 // ポインタ std::basic_string<charT> replace( const charT *const str, const charT *const fmt ) const; // ポインタと文字列長 std::basic_string<charT> replace( const charT *const str, const std::size_t len, const charT *const fmt_text, const std::size_t fmt_len ) const; // basic_string std::basic_string<charT> replace( const std::basic_string<charT> &str, const std::basic_string<charT> &fmt ) const; // コールバック函数による置換 typedef std::basic_string<charT> (*replace_function)(const re_match<charT> &); // ポインタ std::basic_string<charT> replace( const charT *const str, const replace_function repfunc ) const; // ポインタと文字列長 std::basic_string<charT> replace( const charT *str, const std::size_t len, const replace_function repfunc ) const; // basic_string std::basic_string<charT> replace( const std::basic_string<charT> &str, const replace_function repfunc ) const;
ECMAScript仕様書の Runtime Semantics: GetSubstitutionの表に定められているものが書式文字列として使えます。
この表にない文字はそのまま置換後の文字列として使われます。
テキストシンボル | 置換テキスト |
---|---|
$$ |
$ そのもの。 |
$& |
マッチした箇所全体。 |
$` |
マッチした箇所に先行する部分。 |
$' |
マッチした箇所より後方の部分。 |
$1 $2 $3 $4 $5 $6 $7 $8 $9 (後ろに数字が続かぬこと) |
正規表現中の対応する括弧で捕獲された文字列。該当する括弧が何も捕獲していない場合は空文字に置換される。正規表現中のキャプチャ括弧の個数より大きな数が指定された場合は置換されず。 |
$nn (nn は01から99までの範囲) |
正規表現中の対応する括弧で捕獲された文字列。該当する括弧が何も捕獲していない場合は空文字に置換される。正規表現中のキャプチャ括弧の個数より大きな数が指定された場合は置換されず。 |
$<NAME> |
|
// 書式指定による置換例。 #include <cstdio> #include <string> #include "nire.hpp" int main() { const nire::RegExp re("(\\d)(\\d)", "g"); // 数字が2つ続いている箇所を探す。 const std::string rep = re.replace("abc0123456789def", "($2$1)"); // $1と$2との順番を入れ替えて括弧でくくる。 std::printf("Result: %s\n", rep.c_str()); return 0; } ---- 実行結果 ---- Result: abc(10)(32)(54)(76)(98)def
マッチした箇所の情報を函数内部のre_match<charT>
型インスタンスにセットし、それへのconst
参照を引数とするコールバック函数を呼び出します。
コールバック函数の側は、置換後の文字列をstd::basic_string<charT>
型インスタンスにセットしてreturn
する必要があります。なおこちらの方法では前述の書式文字列は使えません。
// コールバック函数による置換例。 #include <cstdio> #include <string> #include "nire.hpp" std::string repfunc1(const nire::Match &m) { return " [" + m[1].str() + ", " + m[2].str() + "] "; } int main() { const nire::RegExp re("(\\d)(\\d)", "g"); const std::string text("abc0123456789def"); std::string rep; rep = re.replace(text, repfunc1); std::printf("Result1(C++98/C++03): %s\n", rep.c_str()); // C++11以降のラムダ式による置換。 rep = re.replace(text, [](const nire::Match &m) -> std::string { return " [" + m[1].str() + ", " + m[2].str() + "] "; }); std::printf("Result2(C++11): %s\n", rep.c_str()); // C++14以降。 rep = re.replace(text, [](const auto &m) { return " [" + m[1].str() + ", " + m[2].str() + "] "; }); std::printf("Result3(C++14): %s\n", rep.c_str()); return 0; } ---- 実行結果 ---- Result1(C++98/C++03): abc [0, 1] [2, 3] [4, 5] [6, 7] [8, 9] def Result2(C++11): abc [0, 1] [2, 3] [4, 5] [6, 7] [8, 9] def Result3(C++14): abc [0, 1] [2, 3] [4, 5] [6, 7] [8, 9] def
JavaScriptのString.prototype.split(RegExpオブジェクト, リミット)
に準拠した動作をします。
検索対象文字列中の「正規表現とマッチする箇所」の前後を分割してstd::basic_string
型インスタンスを作り、それらを配列コンテナに詰め込んでreturn
します。
正規表現の中にキャプチャ括弧があった場合はそれらもすべて配列コンテナに詰め込まれます。何も捕獲していない括弧のところも飛ばされず、空文字列がpushされます。
結果を受け取るコンテナ型は、clear()
, size()
, push_back()
の3つが実装されていれば何でも使えます。戻り値はpush_back()
が行われた回数です。
// ポインタ template <typename ContainerT> std::size_t split( ContainerT &out, const charT *const str, const std::size_t limit = static_cast<std::size_t>(-1), const bool push_remainder = false ) const; // ポインタと文字列長(lengthのほうが先なのはオーヴァーロード解決のため) template <typename ContainerT> std::size_t split( ContainerT &out, const std::size_t len, const charT *const str, const std::size_t limit = static_cast<std::size_t>(-1), const bool push_remainder = false ) const; // basic_string template <typename ContainerT> std::size_t split( ContainerT &out, const std::basic_string<charT> &str, const std::size_t limit = static_cast<std::size_t>(-1), const bool push_remainder = false ) const;
引数のlimit
は分割する回数の上限値です。例えば分割する正規表現が /,/
でlimit
が5
であった場合、検索対象文字列中に5回 ','
を発見した時点で分割処理は終了します。
分割回数の上限が指定できるsplit()
というのはスクリプト言語によくある機能ですが、JavaScriptのsplit()
は少し変わっていて、マッチングがlimit
で指定された回数に達するとそれ以降のまだ調べていない部分の文字列は戻り値の中に含めず、そのまま捨ててしまいます。
この挙動は個人的にあまり嬉しくありませんので、NIREでは独自拡張機能として「分割がlimit
の回数に達した時、それより後ろのまだ調べていない部分の文字列も戻り値の中に含めるかどうか」を指定するpush_remainder
フラグを追加してあります。
push_remainder
がtrue
の時は、最大でlimit + 1
個分の要素が返されてくることになります。これは結果的にPythonのsplit()
と似た挙動になっています。
// limitとpush_remainderフラグの挙動。 #include <cstdio> #include <string> #include <vector> #include "nire.hpp" int main() { const nire::RegExp re(":"); const std::string text("0:12:345:6789:abcdef"); std::vector<std::string> res; // 無制限分割。 re.split(res, text); for (std::size_t i = 0; i < res.size(); ++i) std::printf("%s%s", i == 0 ? "[" : ", ", res[i].c_str()); std::puts("]"); // limit=2. 残りは捨てられる。 re.split(res, text, 2); for (std::size_t i = 0; i < res.size(); ++i) std::printf("%s%s", i == 0 ? "[" : ", ", res[i].c_str()); std::puts("]"); // limit=2 かつ push_remainder=true. 残りは最後に一括してpushされる。 re.split(res, text, 2, true); for (std::size_t i = 0; i < res.size(); ++i) std::printf("%s%s", i == 0 ? "[" : ", ", res[i].c_str()); std::puts("]"); return 0; } ---- 実行結果 ---- [0, 12, 345, 6789, abcdef] [0, 12] [0, 12, 345:6789:abcdef]
インスタンスの内容を交換します。re1.swap(re2)
とnire::swap(re1, re2)
とが定義されています。
以下は正規表現オブジェクト作成時に指定されたフラグを読み出すためのメンバ函数です。函数名はいずれもJavaScriptにおける同名のプロパティー名から来ています。
函数名 | 戻り値 |
---|---|
std::basic_string<charT> flags(); |
パターンコンパイル時に渡されたフラグを文字列化したもの。 |
bool global(); |
nire::flag::global または "g" が指定されたか否か。 |
bool ignoreCase(); |
nire::flag::icase または "i" が指定されたか否か。 |
bool multiline(); |
nire::flag::multiline または "m" が指定されたか否か。 |
bool dotAll(); |
nire::flag::dotall または "s" が指定されたか否か。 |
bool unicode(); |
常にtrue が返る。 |
bool sticky(); |
nire::flag::sticky または "y" が指定されたか否か。 |
RegExp.exec()
によって正規表現検索の結果が収められるクラスです。
JavaScriptのRegExp.exec()の返り値を参考に次のメンバが定義されています。
re_match
型インスタンスは正規表現にマッチしたかどうかを示すbool
値にキャストすることが出来ます。
直前のマッチが成功していた場合、正規表現中に出てくるキャプチャ括弧の数+1が返ってきます。失敗していた場合やインスタンス作成後まだ一度も検索の結果を受け取っていない場合は0
が返ってきます。+1されるのは「正規表現全体にマッチした箇所は暗黙の0番括弧によって捕獲された」と見なされるためです。
C++のクラスでは.size()
という名前のメンバ函数を通して要素の数を取得することが多いのに対して、JavaScriptでは.length
というプロパティーを使って要素の数を読み書きする仕組みになっています。どちらでもお好みで使えるようにNIREではsize()
とlength()
との両方用意してあります。
re_match
型インスタンスをm
とした時、m[0]
には正規表現にマッチした箇所全体の位置情報が収められています。m[1]
以降にはそれぞれ対応する番号の括弧で捕獲された文字列の位置情報が収められています。
引数として指定できるのは、0
からlength()
の値-1までです。
引数が文字列の場合はgname
というグループ名で捕獲された文字列の位置情報を返します。
戻り値であるre_group<charT>
型はstd::basic_string<charT>
にキャストできますので、マッチした部分の情報を直接文字列として受け取ることも可能です。この型の詳細については後述します。
引数で指定されたグループの範囲 [begin, end) のbeginが、検索対象文字列において何番目(0起点)の要素に当たるかを返します。
引数で指定されたグループの範囲 [begin, end) のendが、検索対象文字列において何番目(0起点)の要素に当たるかを返します。
インスタンスの内容を交換します。re1.swap(re2)
とnire::swap(re1, re2)
とが定義されています。
正規表現にマッチした範囲の位置情報を保持する構造体です。この型はis_trivially_copyable
ですのでmemcpy
でコピーすることも出来ます。
この型が保持しているのは位置情報だけですので、文字列を取り出す際には検索対象文字列がその時点でまだメモリ上に存在している必要があります。
次のメンバが定義されています。
re_group
型インスタンスは、内部の位置情報が有効な範囲を指し示しているかどうかを示すbool
値にキャストすることが出来ます。
re_group
内で保持している位置情報を元にstd::basic_string<charT>
型の文字列を作り、それをreturn
します。
保持している範囲がコードユニット何個分になるかを返します。何もキャプチャしていない時は0
が返ります。
例によってsize()
とlength()
との両方用意してあります。
文字列が捕獲されているかどうかを示すbool
値を返します。
re_group<charT>
同士またはstd::basic_string<charT>
との比較用に==
, !=
, <
, <=
, >
, >=
が定義されています。
パターンコンパイル時または照合時にエラーが発生すると、nire::re_error
がthrow
されてきます。
nire::re_error
はstd::runtime_error
の派生クラスで次のメンバが定義されています。
// nire::re_errorのメンバ。 const char *what() const; // std::runtime_errorより継承したもの。 re_error::type code() const; // エラー番号の読み出しを行う。
エラー番号は次の通りです。
エラー値 | エラー内容 | 備考 |
---|---|---|
escape |
不正なエスケープ。\ の後ろに変な文字が続いているか、\ で文字列が終わっている。 |
パターンコンパイル時に発生するエラー。 |
backref |
後方参照エラー。対応するキャプチャ括弧が正規表現中にない。 | |
sqbrack |
[] が非対称。 |
|
paren |
() が非対称。 |
|
brace |
{} が非対称。 |
|
badbrace |
{} 内におかしな文字がある。 |
|
range |
文字クラス内で [b-a] のような範囲指定がされている。- の左側の文字のほうが右側の文字よりコードポイント値が大きい。 |
|
badrepeat |
量指定子 * ? + {n,m} の前に変な文字がある。 |
|
utf8 |
UTF-8として不正なバイト列がある。 | |
complexity |
複雑な照合。 | 照合時に発生するエラー。 |
これらの他、メモリの確保に失敗した時にはstd::bad_alloc
がthrow
されてきます。
C++11以降で導入された機能のうち、NIREが利用することもあるのは以下のものです。
char16_t
型とchar32_t
型char8_t
型2020年 5月現在、NIREではこれらの使用可否を次のようにして判定しています。
#ifdef __cpp_unicode_characters #ifndef NIRE_CPP11_CHAR1632_ENABLED #define NIRE_CPP11_CHAR1632_ENABLED // char16_t, char32_t用のtypedefを行う。 #endif #endif #ifdef __cpp_rvalue_references #ifndef NIRE_CPP11_MOVE_ENABLED #define NIRE_CPP11_MOVE_ENABLED // コンストラクタや代入でmoveを有効にする。 #endif #endif #ifdef __cpp_char8_t #ifndef NIRE_CPP20_CHAR8_ENABLED #define NIRE_CPP20_CHAR8_ENABLED // char8_t用のtypedefを行う。 #endif #endif
該当する機能がお使いのコンパイラでサポートされているにもかかわらず、コンパイラが__cpp_*
マクロを適切に設定しないためにこれらの機能が有効にならない場合、NIREをincludeする前に上記マクロのうち必要なNIRE_CPP_*
を定義しておくと、対応する機能を強制的にオンにすることが出来ます。
NIREではECMAScript 2020 (ES11.0) のRegExp互換の正規表現が使えます(u
フラグは常に指定されていると見なされる)。
具体的には次の通りです。
文字 | |
---|---|
. |
改行以外の文字にマッチ(ECMAScriptにおける改行文字は、U+000A, U+000D, U+2028, U+2029の4文字)。 |
\0 |
NULL文字 ( |
\t |
水平タブ ( |
\n |
Line Feed ( |
\v |
垂直タブ ( |
\f |
Form Feed ( |
\r |
Carriage Return ( |
\cX |
|
\\ |
バックスラッシュそのもの ( |
\xHH |
UTF-16におけるコードユニット値が、2桁の16進数
UTF-16において |
\uHHHH |
Unicodeのコードポイント値が、4桁の16進数
連続する |
\u{H...} |
1桁以上の16進数 |
\ |
|
^$.*+?()[]{}|\/ 以外の文字 |
その文字そのものを表す。 |
選択 | |
A|B |
正規表現AまたはBにマッチ。 |
文字クラス | |
[] |
文字クラス。文字集合。
Perlの正規表現には「
|
定義済み文字クラス | |
\d |
|
\D |
|
\s |
Note: 厳密にはWhiteSpaceとLineTerminatorとに一致します。今後UnicodeカテゴリのZsに新たな文字が追加されることがあれば、その都度WhiteSpaceの右辺値は増えます。 |
\S |
|
\w |
|
\W |
|
\p{...} |
Note: 2020年現在、ECMAScriptもUnicodeも毎年新版がリリースされていますが、Unicodeの新版がリリースされる時期よりもECMAScriptの新版の仕様が確定する時期のほうが少し早いため、ECMAScriptの仕様書に掲載される「 |
\P{...} |
|
量指定子(回数指定) | |
* *? |
直前の正規表現による照合を0回以上繰り返す。
先行する表現なしにいきなり回数指定が現れた時には |
+ +? |
直前の正規表現による照合を1回以上繰り返す。 |
? ?? |
直前の正規表現による照合を0回ないし1回繰り返す。 |
{n} |
直前の正規表現による照合をきっちり
|
{n,} {n,}? |
直前の正規表現による照合を |
{n,m} {n,m}? |
直前の正規表現による照合を
|
括弧・後方参照・グループ化 | |
(...) |
正規表現のグループ化および文字列の捕獲。正規表現全体において開き括弧 括弧自身またはその外側の正規表現に繰り返し指定がある場合、捕獲した文字列はループのたびに未定義値相当にクリアされる。そのためキャプチャした文字列を次のループに持ち越すことは出来ない。 |
\N (※Nは正の 整数) |
後方参照。
ECMAScriptの正規表現では、文字列を捕獲する括弧が対応する後方参照よりも先行している必要はない。そのため
対応する括弧が何も捕獲していない時、後方参照は未定義値 ( |
(?<NAME>...) |
名前付きの |
\k<NAME> |
|
(?:...) |
グループ化。 |
位置にマッチするもの | |
^ |
文字列の最初にマッチ。 |
$ |
文字列の最後にマッチ。 |
\b |
文字クラスの外側では |
\B |
文字クラスの外側では |
(?=...) |
肯定先読み。たとえば |
(?!...) |
否定先読み。たとえば |
(?<=...) |
肯定戻り読み。たとえば |
(?<!...) |
否定戻り読み。たとえば |
'\'
で終わっていたり '\'
がこの表にない組み合わせで使われたりした時は、error_escape
がthrow
されてきます。/\1\u0030/
のように数字のほうはコードポイントで書く」「/\1[0]/
のように、数字のほうはその1文字だけからなる文字クラスとして書く」の2つがあります。NIREのパターンコンパイラはどちらの書き方も同じ内部表現に変換します。\ooo
や \0ooo
のような8進数表現が存在しません。ECMAScriptの仕様では、\
に0
が続く時は<NUL> (\u0000
) として解釈し、1-9で始まる数字が続く時は後方参照として解釈、その際対応する()
が正規表現中に存在しなければエラーと定められています。/(?=\p{sc=Latin})\p{Ll}/
→ ラテン文字の小文字にのみマッチ)。またこの応用で、否定先読みを使うと減算相当の処理も出来ます(例:/(?!\p{sc=Latin})\p{Ll}/
→ ラテン文字ではない小文字にマッチ)。
bRegExp
はバイナリ配列のようなものに対して検索を行うことを想定したもので、入力文字列を「0
から((1 << CHAR_BIT) - 1)
までの値の連続」として解釈します。
// bRegExpのテスト。 #include <cstdio> #include <string> #include "nire.hpp" int main() { // "\xE3\x81\x82" はひらがなの「あ (U+3042)」 // "\xC3\xA3" は ã (U+00E3) const std::string text("\xe3\x81\x82""\xc3\xa3"); nire::RegExp re("\\xe3"); // Unicode値がU+00E3である文字を探す。 nire::bRegExp bre("\\xe3"); // バイト値が0xE3である箇所を探す。 nire::Match mr, mbr; mr = re.exec(text); mbr = bre.exec(text); std::printf("RegExp:%u+%u bRegExp:%u+%u\n", mr.index(), mr[0].length(), mbr.index(), mbr[0].length()); return 0; } ---- 実行結果 ---- RegExp:3+2 bRegExp:0+1
RegExp
型のre
は、U+00E3をUTF-8で表現した箇所を探したのに対してbRegExp
型のbre
は、オクテット値が0xE3
である箇所を探したことが分かります。
ECMAScriptの正規表現(およびその元となったPerlの正規表現)では通常、バックトラッキングと呼ばれる方法を使って照合が行われます。このバックトラッキング方式には、「量指定子(回数指定)が入れ子になっている正規表現」や「量指定子を伴う文字ないし文字クラスが連続していて、かつそれらが互いに排他的な集合になっていない正規表現」で検索を行うと、オートマトンが著しい長考に入ってしまうことがあるという問題が存在します。
次のような例が有名です。
残念ながらこの現象に対しては、あらゆる状況に適用できる根本的な解決策というものが見つかっていません。そこで制御が長時間返ってこなくなる事態を避けるため、NIREは同じ位置からの照合が一定回数以上失敗するとre_error(re_error::type::complexity)
をthrow
するようになっています。
回数の既定値は16777216(256の3乗)ですが、regexp
型インスタンスのbacktrackingLimit
メンバ変数に任意の値を代入することで変更することも出来ます。
以下は外部のサイトです。