SRELL (std::regex-like library) はC++用のUnicode対応正規表現テンプレートライブラリです。
SRELLはECMAScript (JavaScript) 互換の正規表現エンジンを、"std::regex"(C++11で導入された正規表現ライブラリ)とクラス構成が同じになるようにラッピングしたものです。API/クラスデザインが同じですので、std::regexや、その基となったboost::regexと同じように扱えます。
また、ヘッダファイルのみの純粋なテンプレートライブラリですのでincludeするだけですぐに使えます。事前のセットアップやインストールは不要です。
SRELLはUnicodeに特化した正規表現ライブラリです。
'.'
がUTF-16文字列でサロゲートペアの片割れだけにマッチしたり、UTF-8文字列のコードユニットにマッチしたりするようなことがありません。[丈𠀋]
のように指定できます。また [\u{1b000}-\u{1b0ff}]
のような範囲指定もできます。※ちなみにC++11以降のstd::regexは、us-asciiやiso-8859-*のような1文字が固定長の文字コードを前提としています。そのため1文字が可変長であるUTF-8, UTF-16, Shift_JIS, EUC-JPの文字列はうまく扱えません。
SRELLはicase検索(大文字・小文字を区別しない検索)時の速度低下が極力軽減されるようチューニングされています。
C++11に向けた改訂作業の中でもregexは比較的初期に提案された拡張であったことから、まったくと言って良いほどC++11の新機能に依存していません。そのためC++11より前のコンパイラであっても、C++のテンプレートを正しく解釈するものであればSRELLは利用可能です(動作確認済みコンパイラのうちもっとも古いものはVC++2005です)。
パスの通ったところに srell*.h*(srell.hpp, srell_ucfdata2.h, srell_updata3.h の3ファイル)を置いて srell.hpp をincludeするだけです。
// Example 01: #include <cstdio> #include <string> #include <iostream> #include "srell.hpp" int main() { srell::regex e; // 正規表現オブジェクト。 srell::cmatch m; // 結果を納めるオブジェクト。 e = "\\d+[^-\\d]+"; // 正規表現をコンパイル。 if (srell::regex_search("1234-5678-90ab-cdef", m, e)) { // printfを使うなら。 const std::string s(m[0].first, m[0].second); // 上は下のどちらかでも良い。 // const std::string s(m[0].str()); // const std::string s(m.str(0)); std::printf("result: %s\n", s.c_str()); // iostreamを使うなら。 std::cout << "result: " << m[0] << std::endl; } return 0; }
この例のように、SRELLを構成するクラスやアルゴリズムはすべてnamespace srellの下に置かれています。この点を除けば使い方はstd::regexに準じます。
現時点ではまだstd::regexに関する日本語の文書があまりないようですので、さしあたってSRELLを使ううえで必要となりそうな情報を次のページにまとめました。
Zipアーカイヴ内の readme_ja.txt も併せてご覧ください。
ECMAScript仕様書の最新ドラフトのRegExpに定義されている表現が使えます。
既定ではu
フラグは常に指定されていると見なされます。SRELL 4.000以降はv
フラグモード (/.../v
) にも対応していて、パターンコンパイラ (srell::basic_regex
) にunicodesets
フラグを渡すことでvモードがオンになります。vモードの詳細については専用の頁を設けてあります。
対応している表現の詳細は次の通りです(注記なきものはuモード/vモード共通)。
文字 | |
---|---|
. |
既定:改行文字(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進数
SRELL 2.500以降:連続する |
\u{H...} |
1桁以上の16進数
Note: ECMAScript 6にて追加された表現です。提案書の段階では |
\ |
Note: ECMAScriptの |
^$.*+?()[]{}|\/ 以外の文字 |
その文字そのものを表す。 |
選択 | |
A|B |
正規表現AまたはBにマッチ。 |
文字クラス | |
[] |
文字クラス。文字集合。
大文字・小文字を区別しない検索の時(
Perlの正規表現には「
|
vモード(
同じ階層の
注意事項1:vモードでは
注意事項2:次の18種類の二重記号は将来の機能拡張用に予約されています。これらを[]内に書くことは出来ません。使用された場合は
|
|
定義済み文字クラス | |
\d |
|
\D |
|
\s |
Note: 厳密にはWhiteSpaceとLineTerminatorとに一致します。今後UnicodeカテゴリのZsに新たな文字が追加されることがあれば、その都度WhiteSpaceの右辺値は増えます。 |
\S |
|
\w |
|
\W |
|
\p{...} |
vモードでは文字列プロパティー(properties of strings. 複数のコードポイントによって表現される文字列にマッチするUnicode property)にも対応。文字クラス内でも使えるが、補集合のクラス (
Note: ES2018/ES9.0で導入、SRELLは2.000で対応。 |
\P{...} |
先の
Note: ES2018/ES9.0で導入、SRELLは2.000で対応。 |
量指定子(回数指定) | |
* *? |
直前の正規表現による照合を0回以上繰り返す。
先行する表現なしにいきなり回数指定が現れた時には |
+ +? |
直前の正規表現による照合を1回以上繰り返す。 |
? ?? |
直前の正規表現による照合を0回ないし1回繰り返す。 |
{n} |
直前の正規表現による照合をきっちり
|
{n,} {n,}? |
直前の正規表現による照合を |
{n,m} {n,m}? |
直前の正規表現による照合を
|
括弧・後方参照・グループ化 | |
(...) |
正規表現のグループ化および文字列の捕獲/キャプチャ。正規表現全体において開き括弧
括弧自身またはその外側の正規表現に繰り返し指定がある場合、捕獲した文字列はループのたびに未定義値相当にクリアされる。そのためキャプチャした文字列を次のループに持ち越すことは出来ない。例えば |
\N (※Nは正の 整数) |
後方参照。
ECMAScriptの正規表現では、文字列を捕獲する括弧が対応する後方参照よりも先行している必要はない。そのため
対応する括弧が何も捕獲していない時、後方参照は未定義値 ( |
(?<NAME>...) |
名前付きの
Note: ES2018/ES9.0で導入、SRELLは2.000で対応。同じ名前を複数回使える機能 (duplicate named capturing groups) はES2025で導入予定、SRELLは4.043で対応。 |
\k<NAME> |
Note: ES2018/ES9.0で導入、SRELLは2.000で対応。 |
(?:...) |
グループ化。 |
フラグ変更 | |
(?ims-ims:...) |
同じ括弧内の
これらは Note: ES2025で導入予定、SRELLは4.045で実装(ただし既定では無効)、4.058より既定で有効。 |
(?ims-ims) |
正規表現内でフラグ指定をする。
この表現は正規表現の先頭でのみ使用可能(Python 3.11と同じ)。もし他の場所で使われたら
註:SRELL 4.007以降で利用可能。なおこの機能はSRELLの独自拡張で、ECMAScriptの仕様にはありません。この機能は |
位置にマッチするもの | |
^ |
文字列の最初にマッチ。 |
$ |
文字列の最後にマッチ。 |
\b |
文字クラスの外側では |
\B |
文字クラスの外側では |
(?=...) |
肯定先読み。たとえば |
(?!...) |
否定先読み。たとえば |
(?<=...) |
肯定戻り読み。たとえば
Note: SRELL 1では、(...)内は |
(?<!...) |
否定戻り読み。たとえば
Note: SRELL 1では、(...)内は固定幅の文字列のみ指定可能。固定幅でない時は |
'\'
で終わっていたり、'\'
がこの表にない文字との組み合わせで使われたりした時は、error_escape
がthrow
されてきます。後者についてはSRELL 2.300まで「'\' に続く文字そのもの」として解釈されていましたが、2.301以降はECMAScriptの仕様に合わせてエラー扱いするようになりました。/(?:\1)0/
のように後方参照をそれ単体からなるグループにする」「/\1\u0030/
のように数字のほうはコードポイントで書く」「/\1[0]/
のように、数字のほうはその1文字だけからなる文字クラスとして書く」などがあります。SRELLのパターンコンパイラはどの書き方も同じ内部表現に変換します。\ooo
や \0ooo
のような8進数表現が存在しません。ECMAScriptの仕様では、\
に0
が続く時は<NUL> (\u0000
) として解釈し、1-9で始まる数字が続く時は後方参照として解釈、その際対応する()
が正規表現中に存在しなければエラーと定められています。
/(?=\p{sc=Latin})\p{Ll}/
→ ラテン文字の小文字にのみマッチ)。またこの応用で、否定先読みを使うと減算相当の処理 (difference/subtraction) も出来ます(例:/(?!\p{sc=Latin})\p{Ll}/
→ ラテン文字ではない小文字にマッチ)。
Unicode対応のために、次のような型がSRELLには追加されています。
Prefixと文字列の解釈 | Tの型 | basic_regex<T> |
match_results<T> |
sub_match<T> |
備考 |
---|---|---|---|---|---|
u8- |
char8_t または char |
u8regex |
u8cmatch u8smatch |
u8csub_match u8ssub_match |
コンパイラがC++20以降に準拠する時はchar8_t 型、それ以外はchar 型で特殊化。後者の場合は後述するu8c- 型の単なる別名 (typedef ) 。 |
u16- |
char16_t |
u16regex |
u16cmatch u16smatch |
u16csub_match u16ssub_match |
コンパイラがC++11以降に準拠する時のみ定義される。 |
u32- |
char32_t |
u32regex |
u32cmatch u32smatch |
u32csub_match u32ssub_match |
|
u8c- |
char |
u8cregex |
u8ccmatch u8csmatch |
u8ccsub_match u8cssub_match |
|
u16w- |
wchar_t |
u16wregex |
u16wcmatch u16wsmatch |
u16wcsub_match u16wssub_match |
WCHAR_MAX が0xFFFF 以上、0x10FFFF 未満の場合のみ。 |
u32w- |
u32wregex |
u32wcmatch u32wsmatch |
u32wcsub_match u32wssub_match |
WCHAR_MAX が0x10FFFF 以上の場合のみ。 |
|
u1632w- |
u1632wregex |
u1632wcmatch u1632wsmatch |
u1632wcsub_match u1632wssub_match |
WCHAR_MAX の値によって、上記 u16w- または u32w- の別名となる。
|
各prefixの意味するところは次の通りです。
char8_t
型に対応しているかどうか(__cpp_char8_t
定義の有無で判断)で次のように変わります。
char8_t
対応なら:char8_t
型配列またはstd::u8string
型インスタンスをUTF-8文字列として扱う。char8_t
未対応なら:後述のu8c-に同じ。単なる別名としてtypedef
される。u8"..."
) の型がchar
からchar8_t
に変更されましたが、上記の切替によりSRELLのu8-型は常にu8"..."
に適するようになっています。
char16_t
型配列またはstd::u16string
型インスタンスをUTF-16文字列として扱う。
UTF-16の文字列リテラル (u"..."
) に適しています。
char32_t
型配列またはstd::u32string
型インスタンスをUTF-32文字列として扱う。
UTF-32の文字列リテラル (U"..."
) に適しています。
char
型配列またはstd::string
型インスタンスをUTF-8文字列として扱う(SRELL 2.100で導入。SRELL 2.002まではこれがu8-というprefixを使用していました)。wchar_t
型配列またはstd::wstring
型インスタンスをUTF-16文字列として扱う(WCHAR_MAX
が0xFFFF
以上0x10FFFF
未満の時のみ定義される)。wchar_t
型配列またはstd::wstring
型インスタンスをUTF-32文字列として扱う(WCHAR_MAX
が0x10FFFF
以上の時のみ定義)。WCHAR_MAX
が0xFFFF
以上0x10FFFF
未満なら上記u16wに同じ。WCHAR_MAX
が0x10FFFF
以上なら上記u32wに同じ。
上記u16w-
, u32w-
と異なり、このu1632w-
型はWCHAR_MAX
が0xFFFF
以上なら常に定義される。
SRELL 2.930以降で利用可能。
※u16w-
型とu32w-
型とはWCHAR_MAX
の大きさによって排他的に定義されます。そのせいでソースコードのポータビリティーに問題が発生しうるということに後になって気づいたため、SRELL 2.930でu1632w-
が導入されました。
先の表では省略しましたが、このルールに基づいてregex_iterator
, regex_iterator2
, regex_token_iterator
でも同じようにu(8c?|16w?|32w?|1632w)
prefixの付いた型がtypedef
されています。
Unicode対応版の基本的な使い方は次の通りです。
srell::u8regex u8re(u8"UTF-8文字列による正規表現"); srell::u8cmatch u8cm; // 検索対象がbasic_string型なら-smatch。以下同様。 std::printf("%s\n", srell::regex_search(u8"検索対象となるUTF-8文字列", u8cm, u8re) ? "found!" : "not found..."); srell::u16regex u16re(u"UTF-16文字列による正規表現"); srell::u16cmatch u16cm; std::printf("%s\n", srell::regex_search(u"検索対象となるUTF-16文字列", u16cm, u16re) ? "found!" : "not found..."); srell::u32regex u32re(U"UTF-32文字列による正規表現"); srell::u32cmatch u32cm; std::printf("%s\n", srell::regex_search(U"検索対象となるUTF-32文字列", u32cm, u32re) ? "found!" : "not found..."); srell::u1632wregex u1632wre(L"UTF-16またはUTF-32の文字列による正規表現"); srell::u1632wcmatch u1632wcm; std::printf("%s\n", srell::regex_search(L"検索対象となるUTF-16またはUTF-32の文字列", u1632wcm, u1632wre) ? "found!" : "not found..."); srell::u16wregex u16wre(L"UTF-16文字列による正規表現"); srell::u16wcmatch u16wcm; std::printf("%s\n", srell::regex_search(L"検索対象となるUTF-16文字列", u16wcm, u16wre) ? "found!" : "not found..."); // 上3行と下3行とは排他的。wchar_tが21ビット未満なら上、以上なら下。 srell::u32wregex u32wre(L"UTF-32文字列による正規表現"); srell::u32wcmatch u32wcm; std::printf("%s\n", srell::regex_search(L"検索対象となるUTF-32文字列", u32wcm, u32wre) ? "found!" : "not found...");
次のフラグオプションが追加されています。
namespace regex_constants { static const syntax_option_type dotall; // (SRELL 2.000以降) // シングルライン指定。'.' の挙動を変える。ECMAScript 2018 (ES9) 以降のs
フラグ (/.../s
) に相当。 static const syntax_option_type unicodesets; // (SRELL 4.000以降) static const syntax_option_type vmode; // (SRELL 4.066以降。上の別名) // vモードを使用する。 static const syntax_option_type sticky; // (SRELL 4.049以降) // このフラグを指定して作られた正規表現インスタンスは、検索時にmatch_continuous
が // 暗黙のうちにセットされる。ECMAScriptのy
フラグ (/.../y
) に相当。 // 検索対象文字列[begin, end)
のうちbegin
の位置からしか照合が試みられぬので、 //regex_iterator
,regex_iterator2
,regex_token_iterator
などでは使えぬことに注意。 static const syntax_option_type quiet; // (SRELL 4.066以降) // コンパイル時、検索時ともにregex_error
型の例外をthrow
しなくなる。 // コンパイル時のエラーはbasic_regex::ecode()
にて、検索時のエラーはmatch_results::ecode()
にて // それぞれ読み出し可能。 }
他のsyntax_option_type
型の値と同様に、これらの値はbasic_regex
内でも定義されています。
sticky
フラグのメリットは、regex_search()
にmatch_continuous
フラグを渡した時(またはregex_match()
使用時)には利用されず無駄になる最適化処理がコンパイル時に飛ばされることです。そのためパターンコンパイル処理が通常より早く終わることが期待できます。
次のerror_type値が追加されています。
namespace regex_constants { static const error_type error_utf8; // (SRELL 2.630以降) //basic_regex
に渡された正規表現中に、不正なUTF-8のシークウェンスが見つかった。 static const error_type error_property; // (SRELL 3.010以降) //\p{...}
または\P{...}
で、対応していないUnicodeプロパティー名または値が指定された。 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モードのみ) // 文字列の補集合を使おうとした。 //\P{POSName}
、[^\p{POSName}]
、[^\q{strings}]
(POSName
は文字列プロパティー名)が見つかった。 static const error_type error_modifier; // (SRELL 4.007以降) // 埋込フラグの (?ims-ims) 形式を先頭以外で使った。または一つの括弧内で同じフラグを複数回指定した。 }
SRELL 4.034以降では #define SRELL_NO_THROW
を定義または -DSRELL_NO_THROW
をコンパイラオプションに追加すると、エラー発生時にregex_error
型の例外をthrow
しなくなります。コンパイル時にregex_error
型をthrow
するコードが無効化され、同型をthrow
する仕組みそのものが実行ファイルへ出力されなくなるためです。
このモードにおいては、コンパイル時に発生したエラーはbasic_regex::ecode()
によって、検索時に発生したエラーはmatch_results::ecode()
によって、それぞれ読み出すことが出来ます。
エラー発生時には、アルゴリズム函数 (regex_search()
, regex_match()
) は false
を返します。
なお SRELL_NO_THROW
を定義しても、メモリ確保失敗時にはstd::bad_alloc
がthrow
されます。抑止できるのはregex_error
型の例外のみです。
regex_iterator
, regex_iterator2
においては、検索時にエラーが発生すると即時にend-of-sequenceイテレータになります。これらのイテレータは内部に持つmatch_results
型インスタンスを指し示してしますので、反復が完了してend-of-sequenceになったのか、エラーによってそうなったのかはit->ecode()
にて判別可能です。
一方regex_token_iterator
はsub_match
を指しているため、その外側のmatch_results::ecode()
にアクセスする方法がありません。そこでversion 4.065でecode()
メンバをregex_token_iterator
クラスに追加しました。
SRELL 2.600以降、引数としてBidirectionalIterator
を3つ取るオーヴァーロードが追加されています。
template <class BidirectionalIterator, class Allocator, class charT, class traits> bool regex_search( BidirectionalIterator first, BidirectionalIterator last, BidirectionalIterator lookbehind_limit, match_results<BidirectionalIterator, Allocator> &m, const basic_regex<charT, traits> &e, const regex_constants::match_flag_type flags = regex_constants::match_default);
3つ目のイテレータであるlookbehind_limit
は、戻り読み (lookbehind) が行われる際の「逆行して良い限界位置」を指定するためのものです。
違う言い方をしますと、この3イテレータ版では「検索対象範囲 [lookbeind_limit, last)
の途中、first
から検索を始める」という処理が行われます。
const char text[] = "0123456789abcdefghijklmnopqrstuvwxyz"; const char* const begin = text; const char* const end = text + std::strlen(text); const char* const first = text + 10; // 'a' の位置に合わせる。 const srell::regex re("(?<=^\\d+)."); srell::cmatch match; std::printf("matched %d\n", srell::regex_search(first, end, match, re)); // 戻り読みも [first, end) の範囲でのみ行われるのでマッチしない。 std::printf("matched %d\n", srell::regex_search(first, end, begin, match, re)); // beginまで逆行できるのでマッチする。 // 即ち3イテレータ版は、[begin, end) というシークウェンスに対して // firstよりsearchを始める。
上の例にもあります通り3イテレータ版では、^
は first
(第1引数として渡したほう)ではなく begin
(第3引数として渡したほう)にマッチするようになります。
また3イテレータ版使用時には、match_results
のposition()
は第3引数として渡した位置からの距離を返すようになります。これに対してmatch_results
のprefix().first
には、第1引数として渡した位置がセットされます。
match_results
を引数に取らないoverloadはAPI簡素化のため4.065で廃止しました。
SRELL 4.065以降、検索対象がbasic_string
である場合、検索開始位置を指定できるオーヴァーロードが追加されています。
template <class ST, class SA, class Allocator, class charT, class traits> bool regex_search( const std::basic_string<charT, ST, SA> &s, const std::size_t start, match_results<typename std::basic_string<charT, ST, SA>::const_iterator, Allocator> &m, const basic_regex<charT, traits> &e, const regex_constants::match_flag_type flags = regex_constants::match_default);
これはregex_search(s.begin() + start, s.end(), s.begin(), m, e, flags)
と書いた場合と同じように振る舞います。
SRELL 2.000以降では名前付きキャプチャ (named-capture) 用に、次のメンバ函数がmatch_results
クラスに追加されています。
difference_type length(const string_type &sub) const;
difference_type position(const string_type &sub) const;
string_type str(const string_type &sub) const;
const_reference operator[](const string_type &sub) const;
// 以下はSRELL 2.650以降。
difference_type length(const char_type *sub) const;
difference_type position(const char_type *sub) const;
string_type str(const char_type *sub) const;
const_reference operator[](const char_type *sub) const;
使い方は<regex>にある同名の函数と同じです。ただ引数として括弧の番号ではなく括弧の名前を渡すという点のみが異なります。
// 使用例。 srell::regex e("-(?<digits>\\d+)-"); srell::cmatch m; if (srell::regex_search("1234-5678-90ab-cdef", m, e)) { const std::string by_number(m.str(1)); // std::regexにもある括弧の番号を使ったアクセス。 const std::string by_name(m.str("digits")); // 名前を使って同じ括弧へアクセス。SRELLの独自拡張。 std::printf("results: bynumber=%s byname=%s\n", by_number.c_str(), by_name.c_str()); } // results: bynumber=5678 byname=5678 と表示される。
Version 4.033まで:正規表現中に存在しないグループ名を渡した場合、error_backref
がthrow
されます。
Version 4.034以降:正規表現中に存在しないグループ名を渡した場合にもエラー扱いとはならず、matched
メンバがfalse
になっているsub_match
型インスタンスへの参照が返ってきます。
名前付きキャプチャ用に $<NAME>
というシンボルが追加されています。
テキストシンボル | 置換テキスト |
---|---|
$$ |
$ そのもの。 |
$& |
マッチした箇所全体。 |
$` |
マッチした箇所に先行する部分。 |
$' |
マッチした箇所より後方の部分。 |
$1 $2 $3 $4 $5 $6 $7 $8 $9 (後ろに数字が続かぬこと) |
正規表現中の対応する括弧で捕獲された文字列。該当する括弧が何も捕獲していない場合は空文字に置換される。正規表現中のキャプチャ括弧の個数より大きな数が指定された場合は置換されず。 |
$nn (nn は01から99までの範囲) |
正規表現中の対応する括弧で捕獲された文字列。該当する括弧が何も捕獲していない場合は空文字に置換される。正規表現中のキャプチャ括弧の個数より大きな数が指定された場合は置換されず。 |
$<NAME> |
名前付きキャプチャ用の追加分
|
直前に呼んだアルゴリズム函数内で発生したエラー値を返します。SRELL 4.034以降に実装されている「例外なしモード」用です。
返されるエラー値は、regex_error::code()
と同じerror_type
型の整数です。直前のアルゴリズム函数呼び出しでエラーが発生していない時は0
を返します。
// std::regex互換のエラー処理。 srell::cmatch cm; try { srell::regex re("a*"); regex_search(text, cm, re); } catch (const srell::regex_error &e) { // エラー処理。 }
// No throw/exceptionモードのエラー処理。 srell::smatch m; srell::regex re("a*"); regex_search(text, m, re); if (m.ecode()) // 非0ならエラーが発生した。 // エラー処理。
SRELL 4.009以降、SRELLのbasic_regex
クラスには次のメンバ函数が独自拡張して追加されています。
srell::regex_match()
と同じ処理を行います。basic_regex
型インスタンスをre
とした時、re.match(...)
は、srell::regex_match(..., re, ...)
のように書いたのと同じです。
以下のようなoverload函数があります。引数の順番は、引数re
が抜かされる点を除いてregex_match()
のものと同じです。
template <typename BidirectionalIterator, typename Allocator> bool match( const BidirectionalIterator begin, const BidirectionalIterator end, match_results<BidirectionalIterator, Allocator> &m, const regex_constants::match_flag_type flags = regex_constants::match_default) const; // srell::regex_match(begin, end, m, re, flags) に同じ。 | (1) |
template <typename Allocator> bool match( const charT *const str, match_results<const charT *, Allocator> &m, const regex_constants::match_flag_type flags = regex_constants::match_default) const; // srell::regex_match(str, m, re, flags) に同じ。 | (2) |
template <typename ST, typename SA, typename MA> bool match( const std::basic_string<charT, ST, SA> &s, match_results<typename std::basic_string<charT, ST, SA>::const_iterator, MA> &m, const regex_constants::match_flag_type flags = regex_constants::match_default) const; // srell::regex_match(s, m, re, flags) に同じ。 | (3) |
// Version 4.069以降。 template <typename MA> bool match( const contiguous_container_view c, match_results<const charT *, MA> &m, const regex_constants::match_flag_type flags = regex_constants::match_default) const; | (4) ※4.069以降。 |
(4) のcontiguous_container_view
はstd::string_view
風のviewクラスです。その名の通りcontiguousなコンテナなら何でも引数c
として渡せます。詳しくはsearch()
にて解説しています。
2025/02/14追記:Version 4.057で誤って削除してしまっていました。Version 4.064で引数にmatch_results
を取るoverloadsのみ復活させ、match_results
を取らぬoverloadsについてはそのまま廃止としました。
srell::regex_search()
と同じ処理を行います。basic_regex
型インスタンスをre
とした時、re.search(...)
は、srell::regex_search(..., re, ...)
のように書いたのと同じです。
以下のようなoverload函数があります。引数の順番は、引数re
が抜かされる点を除いてregex_search()
のものと同じです。
template <typename BidirectionalIterator, typename Allocator> bool search( const BidirectionalIterator begin, const BidirectionalIterator end, match_results<BidirectionalIterator, Allocator> &m, const regex_constants::match_flag_type flags = regex_constants::match_default) const; // srell::regex_search(begin, end, m, re, flags) に同じ。 | (1) |
template <typename Allocator> bool search( const charT *const str, match_results<const charT *, Allocator> &m, const regex_constants::match_flag_type flags = regex_constants::match_default) const; // srell::regex_search(str, m, re, flags) に同じ。 | (2) |
template <typename ST, typename SA, typename MA> bool search( const std::basic_string<charT, ST, SA> &s, match_results<typename std::basic_string<charT, ST, SA>::const_iterator, MA> &m, const regex_constants::match_flag_type flags = regex_constants::match_default) const; // srell::regex_search(s, m, re, flags) に同じ。 | (3) |
// 以下はstd::regex_search()にはありません。 | |
template <typename BidirectionalIterator, typename Allocator> bool search( const BidirectionalIterator begin, const BidirectionalIterator end, const BidirectionalIterator lookbehind_limit, match_results<BidirectionalIterator, Allocator> &m, const regex_constants::match_flag_type flags = regex_constants::match_default) const; // srell::regex_search(begin, end, lookbehind_limit, m, re, flags) に同じ。 | (4) |
// Version 4.065以降。 template <typename ST, typename SA, typename MA> bool search( const std::basic_string<charT, ST, SA> &s, const std::size_t start, match_results<typename std::basic_string<charT, ST, SA>::const_iterator, MA> &m, const regex_constants::match_flag_type flags = regex_constants::match_default) const; // srell::regex_search(s, start, m, re, flags) に同じ。 | (5) ※4.065以降。 |
// Version 4.069以降。 template <typename MA> bool search( const contiguous_container_view c, match_results<const charT *, MA> &m, const regex_constants::match_flag_type flags = regex_constants::match_default) const; | (6) ※4.069以降。 |
// Version 4.069以降。 template <class MA> bool search( const contiguous_container_view c, const std::size_t start, match_results<const charT *, MA> &m, const regex_constants::match_flag_type flags = regex_constants::match_default) const; | (7) ※4.065以降。 |
(6)(7) のcontiguous_container_view
はstd::string_view
風のviewクラスです。
引数として渡せるコンテナの条件は、1) 要素がcontiguousに保持されていること、2) その先頭アドレスを返すdata()
メンバ函数と、長さを返すsize()
メンバ函数とがあることです。std::basic_string_view
の他、std::vector
(data()
メンバを持つのはC++11以降)、std::array
、std::basic_string
などが該当します。
(6)(7)のようなcontiguous_container_view
を引数に取るoverloadsを使う時は、match_results
はcmatch
系を使用します(smatch
系はstd::basic_string
のイテレータ専用です)。
std::basic_string
をsmatch
系とセットで引数として渡した場合は(3)ないし(5)が呼ばれ、cmatch
系と渡した場合は(6)ないし(7)が呼ばれます。
2025/02/14追記:Version 4.057で誤って削除してしまっていました。Version 4.064で引数にmatch_results
を取るoverloadsのみ復活させ、match_results
を取らぬoverloadsについてはそのまま廃止としました。
直前のパターンコンパイルで発生したエラー値を返します。SRELL 4.034以降に実装されている「例外なしモード」用です。
返されるエラー値は、regex_error::code()
と同じerror_type
型の整数です。直前のコンパイルでエラーが発生していない時は0
を返します。
// std::regex互換のエラー処理。 try { srell::regex re("a{2,1}"); } catch (const srell::regex_error &e) { // e.code() == srell::regex_constants::error_badbrace }
// No throw/exceptionモードのエラー処理。 srell::regex re("a{2,1}"); // re.ecode() == srell::regex_constants::error_badbrace
SRELL 4.013以降、regex_iterator2
が追加されています。Grep, replace, splitなどをこれ1つでまかなえるように、regex_iterator
に次のような変更を加えたものです。
match_not_null | match_continuous
を付加して、その位置から再度regex_search()
する)を削除。assign()
を追加。template <typename BidirectionalIterator, typename BasicRegex = basic_regex<typename std::iterator_traits<BidirectionalIterator>::value_type, regex_traits<typename std::iterator_traits<BidirectionalIterator>::value_type> >, typename MatchResults = match_results<BidirectionalIterator> > class regex_iterator2 { typedef typename std::iterator_traits<BidirectionalIterator>::value_type char_type; typedef BasicRegex regex_type; typedef MatchResults value_type; typedef std::ptrdiff_t difference_type; typedef const value_type *pointer; typedef const value_type &reference; typedef std::input_iterator_tag iterator_category; // 以下、メンバー函数……
見にくくて分かりづらいかもしれませんが、テンプレート引数の2つ目はbasic_regex
の型、3つ目はmatch_results
の型です。regex_iterator
よりも単純化してあります。
regex_iterator
に倣って次のtypedef
が定義済みです。
typedef regex_iterator2<const char *> cregex_iterator2; typedef regex_iterator2<const wchar_t *> wcregex_iterator2; typedef regex_iterator2<std::string::const_iterator> sregex_iterator2; typedef regex_iterator2<std::wstring::const_iterator> wsregex_iterator2; // charでUTF-8文字列を処理する。 typedef regex_iterator2<const char *, u8cregex> u8ccregex_iterator2; typedef regex_iterator2<std::string::const_iterator, u8cregex> u8csregex_iterator2; // char16_t, char32_t利用可能時のみ定義。 typedef regex_iterator2<const char16_t *> u16cregex_iterator2; typedef regex_iterator2<const char32_t *> u32cregex_iterator2; typedef regex_iterator2<std::u16string::const_iterator> u16sregex_iterator2; typedef regex_iterator2<std::u32string::const_iterator> u32sregex_iterator2; // char8_t利用可能時のみ定義。 typedef regex_iterator2<const char8_t *> u8cregex_iterator2; // std::u8string利用可能時のみ定義。 typedef regex_iterator2<std::u8string::const_iterator> u8sregex_iterator2; // char8_tがない時のみ定義。 typedef u8ccregex_iterator2 u8cregex_iterator2; // std::u8stringがない時のみ定義。 typedef u8csregex_iterator2 u8sregex_iterator2; // WCHAR_MAXが0x10FFFF以上の時のみ定義。 typedef wcregex_iterator2 u32wcregex_iterator2; typedef wsregex_iterator2 u32wsregex_iterator2; typedef u32wcregex_iterator2 u1632wcregex_iterator2; typedef u32wsregex_iterator2 u1632wsregex_iterator2; // WCHAR_MAXが0xFFFF以上、10FFFF未満の時のみ定義。 typedef regex_iterator2<const wchar_t *, u16wregex> u16wcregex_iterator2; typedef regex_iterator2<std::wstring::const_iterator, u16wregex> u16wsregex_iterator2; typedef u16wcregex_iterator2 u1632wcregex_iterator2; typedef u16wsregex_iterator2 u1632wsregex_iterator2;
regex_iterator
と同じように、end-of-sequenceイテレータを作る「引数なし版」と、通常版とがあります。
regex_iterator2() {} // End-of-sequence iteratorを作る。 | (1) |
regex_iterator2( const BidirectionalIterator a, const BidirectionalIterator b, const regex_type &re, const regex_constants::match_flag_type m = regex_constants::match_default); | (2) |
イテレータのインスタンスを再作成します。引数は通常版のコンストラクタと同じです。
void assign( const BidirectionalIterator a, const BidirectionalIterator b, const regex_type &re, const regex_constants::match_flag_type m = regex_constants::match_default); | (1) |
Iteratingが終端まで行ったならtrue
を、まだならfalse
を返します。
bool done() const;
regex_iterator
と同じように、引数なしのコンストラクタでend-of-sequenceイテレータを作り、それとの比較がtrueになるまでfor
ループでぐるぐる回すという方法も使えますが、このdone()
を使うともっと簡単に判定できます。
srell::sregex_iterator2 eit; srell::sregex_iterator2 it(text.begin(), text.end, re); // for (; it != eit; ++it) { // 下と同じ。 for (; !it.done(); ++it) { // 何かする。 }
コンストラクタに渡した範囲がstd::basic_string
型インスタンスの一部であり、かつイテレータ作成後に余所でサイズ変更をしていない(メモリの割り当て位置が移動していない)場合は、イテレータのreplace()
メンバ函数により、現在マッチしている箇所 ((*it)[0]
) を別の文字列に置き換えることが出来ます。
regex_iterator2::replace()
はstd::basic_string
型の文字列全体のインスタンスを第1引数、置換用文字列を第2引数として取ります。
// [entire.begin(), entire.end()) 内の // [(*it)[0].first, (*it)[0].second) の範囲を // replacementないし[begin, end)に置換する。 | |
template <typename ST, typename SA> void replace(std::basic_string<char_type, ST, SA> &entire, const std::basic_string<char_type, ST, SA> &replacement); | (1) |
template <typename ST, typename SA> void replace(std::basic_string<char_type, ST, SA> &entire, BidirectionalIterator begin, BidirectionalIterator end); | (2) |
template <typename ST, typename SA> void replace(std::basic_string<char_type, ST, SA> &entire, const char_type *const replacement); | (3) |
置換により検索対象文字列が伸び縮みした場合はそれに合わせてイテレータ内部の位置情報を修正し、またメモリの再割り当てが発生した場合は位置情報を自動的に作り直します。
regex_iterator2::replace()
の使用例と、regex_iterator
との違いとを示すサンプルプログラムです。
#include <cstdio> #include <string> #include <regex> #include "srell.hpp" template <typename Iterator, typename Regex> void replace(const Regex &re, const std::string &text, const char *const title) { std::string::const_iterator prevend = text.begin(); Iterator it(text.begin(), text.end(), re), eit; std::string out; for (; it != eit; ++it) { out += it->prefix(); out += "."; prevend = (*it)[0].second; } const std::string::const_iterator end = text.end(); out.append(prevend, end); std::printf("[%s] by %s\n", out.c_str(), title); } int main() { std::string text("a1b"); std::regex re1("\\d*?"); srell::regex re2("\\d*?"); replace<std::sregex_iterator>(re1, text, "std::sregex_iterator"); replace<srell::sregex_iterator>(re2, text, "srell::sregex_iterator"); replace<srell::sregex_iterator2>(re2, text, "srell::sregex_iterator2"); srell::sregex_iterator2 it(text, re2); for (; !it.done(); ++it) it.replace(text, "."); // replace()の使用。 std::printf("[%s] by srell::sregex_iterator2::replace()\n", text.c_str()); return 0; } ---- 実行結果 ---- [.a...b.] by std::sregex_iterator [.a...b.] by srell::sregex_iterator [.a.1.b.] by srell::sregex_iterator2 [.a.1.b.] by srell::sregex_iterator2::replace()
regex_iterator
を使った置換では前述の特例により "1" の部分が置換されてしまっているのに対して、JavaScript互換の振る舞いをする下2つではそのまま残っています。
ちなみにこのstd::regex_iterator
の振る舞いは何に準拠したものなのか不明です。Perlの挙動とも異なっているようです。std::regexの元となったboost::regexは初期からずっとPerl指向ですので、ひょっとすると過去のPerlのいずれかの版に合わせた挙動なのかもしれません。
イテレータがマッチした箇所のprefixと、最後にマッチした箇所より後ろの部分を集めるとsplit相当の処理となります(下図参照。it
はイテレータ)。
対象文字列 | 間 | マッチ1箇所目 | 間 | マッチ2箇所目 | 間 |
---|---|---|---|---|---|
イテレータ it | 1回目の it->prefix() |
(*it)[0] | 2回目の it->prefix() |
(*it)[0] | 2回目の it->suffix() |
そこでsplitにも対応できるように、上図の青いところを集めやすくするためのヘルパ函数を用意しました。
bool split_ready(); // 現在のit->prefix()
がsplitされた文字列として有効な範囲を // 指しているか否かを返す。判断基準はECMAScriptのsplit()準拠 // (it->prefix().first != (*it)[0].second
)。 // 即ち、正規表現が0幅にマッチしうる時のみfalseが返ってくることもある。 const typename value_type::value_type &remainder(bool only_after_match = false); // 繰り返し完了または中断時点の残余区間を取り出す。上表の「2回目のit->suffix()」の部分。 // 戻り値value_type::value_type
はsub_match
型。 // イテレータit
が一度もマッチしていない場合、it->suffix()
は未定義値を返すのに対して、 // このit.remainder()
は常に有効な範囲を返す。 // 引数がtrue
かつ前回のマッチが成功していたら、マッチした位置の終わり ((*it)[0].second
) 以降を返す。 // それ以外は検索開始位置 (it->prefix().first
) 以降を返す。
もっとも単純なsplit処理は次の通りです。
for (; !it.done(); ++it) { if (it.split_ready()) list.push_back(it->prefix()); } list.push_back(it.remainder());
他言語のsplitによくある「正規表現に括弧が含まれていた場合は、それがキャプチャしたものも追加する」処理や、分割回数 (LIMIT) を指定する機能も追加したい場合は次のように書きます。
for (std::size_t count = 0; !it.done(); ++it) { if (it.split_ready()) { if (++count == LIMIT) break; list.push_back(it->prefix()); // *1 for (std::size_t i = 1; i < it->size(); ++i) { if (++count == LIMIT) { list.push_back(it.remainder(true)); // 上 (*1) でpush済みのprefix()区間を除外するため // trueにする。 return; } list.push_back((*it)[i]); } } } list.push_back(it.remainder());
ヘルパーを使っても長くなってしまうので、さらなるヘルパ函数を用意しました。
上のコードは次のように書けます。
std::size_t count = 0; for (it.split_begin(); !it.done(); it.split_next()) { if (++count == LIMIT) break; list.push_back(it.split_range()); } list.push_back(it.split_remainder()); // remainder()ではなくsplit_remainder()であることに注意。
void split_begin(); //split_ready()
がtrue
を返す最初の位置まで移動する。 // このメンバ函数は最初に一回だけ呼ばれることを想定している。 bool split_next(); //split_ready()
がtrue
を返す次の位置まで移動する。 // そのような位置に移動できたらtrue
を、done() == true
になってしまったら //false
を返す。このメンバ函数は通常のoperator++()
の代わりに使われることを意図している。 const typename value_type::value_type &split_range() const; // イテレータが現在指し示している範囲(sub_match
型インスタンスへの参照)を返す。 const typename value_type::value_type &split_remainder(); // Iteratingが終わった後もしくは中断した後にこのメンバ函数を呼ぶと、 // 最後にマッチした位置以降の範囲(sub_match
型インスタンスへの参照)が返ってくる。 // 上のremainder()
とは異なり、only_after_match
に相当するbool値は自動的に計算される。 // Version 4.049以降。 const typename value_type::value_type &split_aptrange(); //done()
がfalse
を返す時はreturn split_range()
する。 //true
ならreturn split_remainder()
する。
分割したものすべてに対して同じ処理をしたい時向けに、SRELL 4.049以降にはsplit_aptrange()
が追加されています。
for (it.split_begin();; it.split_next()) { list.push_back(it.split_aptrange()); // これは次のコードに同じ。 // list.push_back(!it.done() ? it.split_range() : it.split_remainder()); if (it.done()) break; }
他には次のようなメンバ函数があります。
regex_iterator2 &operator=(const regex_iterator2 &right); bool operator==(const regex_iterator2 &right) const; bool operator!=(const regex_iterator2 &right) const; const value_type &operator*() const; const value_type *operator->() const; regex_iterator2 &operator++() regex_iterator2 operator++(int)
前述の通りoperator++()
に特例がないことを除いては、regex_iterator
の同名メンバ函数と同じように振る舞います。
註・ここに明記されていないAPIがあった場合、それらはすべて試験的なものです。予告なく仕様が変更されたり時には削除されたりすることもあり得ます。
直前の検索でエラーが発生していたらその値を返します。SRELL 4.034以降に実装されている「例外なしモード」用です。
返されるエラー値は、regex_error::code()
と同じerror_type
型の整数です。直前の検索でエラーが発生していない時は0
を返します。
ECMAScriptの正規表現(およびその元となったPerlの正規表現)では通常、バックトラッキングと呼ばれる方法を使って照合が行われます。このバックトラッキング方式には、「繰り返しが入れ子になっている正規表現」や「量指定子(回数指定)を伴う文字ないし文字クラスが連続していて、かつそれらが互いに排他的な集合になっていない正規表現」で検索を行うと、オートマトンが著しい長考に入ってしまうことがあるという問題が存在します。
次のような例が有名です。
残念ながらこの現象に対しては、あらゆる状況に適用できる根本的な解決策というものが見つかっていません。そこで制御が長時間返ってこなくなる事態を避けるため、SRELLは特定の位置からの照合が一定回数以上失敗すると、regex_error(regex_constants::error_complexity)
を throw
するようになっています。
回数の既定値は2097152(1 << 21. 128の3乗。SRELL 4.054までは16777216=256の3乗)ですが、アルゴリズム函数(regex_search()
やregex_match()
など)に渡すbasic_regex
型インスタンスのlimit_counter
変数に任意の値を代入することで変更することも出来ます。
SRELLのパターンコンパイラは次の場合に自分自身を再帰呼び出しします。
[]
の中にある別の[]
をパーズする時。
通常、函数の呼び出しにはスタックが使用されますので、正規表現中に入れ子になったグループや文字クラスがあり、その階層があまりに深いとスタック溢れが発生してプログラムが止まってしまうということも起こりえます。
これを防止するため、SRELL 4.065以降は再帰呼び出しの深さがSRELL_MAX_DEPTH
を超えると、error_complexity
をthrow
するようになっています。
SRELL_MAX_DEPTH
の既定値は256ですが、SRELLをincludeする前に#define SRELL_MAX_DEPTH 1024
のようにして先に定義したり、コンパイラオプションに-DSRELL_MAX_DEPTH=512
のように指定したりすることで、値を上書きすることも可能です。
<regex>は、次の6つの正規表現エンジンから選択する仕組みになっています(既定はECMAScript)。
ECMAScript (既定) |
ECMAScriptの正規表現からUnicodeに依存した要素(\sの定義など)を取り除き、代わりにlocale依存の処理とC++の規格書29.13 [re.grammar] にある拡張とを追加したもの。
|
---|---|
basic |
POSIXのbasic regular expression相当。 |
extended |
POSIXのextended regular expression相当。 |
awk |
POSIXのawk相当。 |
grep |
POSIXのgrep相当。 |
egrep |
POSIXのegrep相当。 |
一方SRELLにはこのような選択肢はなく、常にECMAScript互換のエンジンで動きます。この関係で、<regex>で定義されているフラグオプションのうち次のものは、たとえ指定してもSRELLでは無視されます。
syntax_option_type
(かつbasic_regex
のflag_type
)nosubs
, optimize
, collate
, basic
, extended
, awk
, grep
, egrep
(即ちicase
, multiline
以外のすべて)match_flag_type
match_any
, format_sed
<regex>のECMAScriptモードとSRELLとの違いは次の通りです。
同じECMAScriptベースでありながらも<regex>とSRELLとはどちらもどちらの上位互換になっていません。
余分なオーヴァーヘッドを避けるため、SRELLでは次のメンバ函数の実装が簡略化されています。
basic_regex::assign()
: <regex>では例外がthrow
された場合(文字列からのコンパイルに失敗した場合)、*this
は元の値が維持されることになっていますが([re.regex.assign] の11)、SRELLでは*this
はclearされてしまいます。これはコンパイル開始時に元の内容を退避しないためです。SRELL_STRICT_IMPL
を定義するとstd::regex
と同じ挙動になります。
match_results::operator[](size_type n)
: <regex>はn >= match_results::size()
(つまり配列の範囲外アクセス)でも安全性を保証していますが([re.results.acc] の8)、SRELL 4.033まででは保証されません。安全性を保証するには、範囲外アクセスに備えるためだけにダミーのsub_match
型メンバ変数が必要となるためです。
同じ正規表現パターンによる検索を複数回行う時は、basic_regex
型インスタンスの宣言時にstatic const
を付け、パターンコンパイルが一回で済むようにすると高速化が期待できます。
// プログラム中何度も呼ばれる函数。 bool is_included(const std::string &text, const std::string ®ex) { static const srell::regex r1("a*b"); // OK. パターンコンパイルは最初の1回だけで済む。 // srell::regex r2("a*b"); // 毎回パターンコンパイルが行われる。 ...
またループ内でアルゴリズム函数 (regex_search()
, regex_match()
) を複数回呼ぶ場合は、たとえ使わなくてもmatch_results
型インスタンスを渡すようにすると高速化が期待できます。
std::vector<std::string> lines; srell::regex re("https?://\\S+"); // URLっぽいものにマッチする正規表現。 srell::smatch match; // match_results<std::string::const_iterator> の typedef. // ここでlines内にテキストを読み込む。 for (std::size_t i = 0; i < lines.size(); ++i) { // regex_search()内で使い捨てのmatch_resultsが毎回用意されるので遅くなる。 // if (srell::regex_search(lines[i], re)) // *1 if (srell::regex_search(lines[i], match, re)) // *2 ++count; ...
速度が向上する理由は、match_results
内には正規表現照合時に使用するスタックが存在するためです。*1のほうは函数が呼ばれるたびに毎回regex_search()
内で使い捨てのmatch_results
が用意されてスタック用のメモリ割り当て、解放が行われるのに対し、*2のほうは前回割り当てたメモリが次回以降もそのまま流用されますので、繰り返し回数が多い場合には*1に比べて倍以上速くなることもあります。
srell.hppをincludeする前に下記のマクロを定義しておくことで、使わない機能を切り離すことが出来ます。これにより出力されるバイナリのサイズを小さくし、またコンパイル時間(正規表現のではなくC++の)も短くすることが出来ます。
SRELL_NO_UNICODE_ICASE |
大文字小文字を区別しない検索(icase検索)時に使用するUnicodeのcase foldingデータを切り離します。 |
---|---|
SRELL_NO_UNICODE_PROPERTY |
Unicodeプロパティー用のデータを切り離します。この場合
このマクロが定義されると、後述する |
SRELL_NO_UNICODE_DATA |
|
SRELL_NO_NAMEDCAPTURE |
名前付きキャプチャ用のコードを切り離します。 |
SRELL_NO_VMODE |
vモード用のコードを切り離します(SRELL 4.000~4.053のみ)。 |
SRELL_NO_UNICODE_POS |
vモードのproperties of strings用データを切り離します。 |
Version 4.061以降では、実行時にSSE 4.2が利用可能であればSIMD命令を使うようになっています。この機能は次のコンパイラでx86/x64向けにコンパイルした場合に有効になります。
SSE 4.2に対応したのはGCCは4.3、LLVM/Clangは2.6ですので、上記のヴァージョンより前のものでも-msse4.2
をつけてコンパイルすればアクセラレーションは有効になります。
ただし-msse4.2
をつけてコンパイルすると、コンパイラはSSE 4.2までの全SIMD命令をSRELLのコード全体において使って良いものと判断しますので、生成された実行ファイルはSSE 4.2に対応していないCPU上では動かないものになる可能性があります。
SIMDの利用はあらかじめ#define SRELL_NO_SIMD
を定義しておくか、コンパイルオプションに-DSRELL_NO_SIMD
を指定することにより無効化できます。
※SSE4.2はIntelのCore i3/i5/i7は2008年登場のNehalem世代以降、Pentium/Celeronは2011年登場のSandy Bridge世代以降、AMDは2011年登場のFXシリーズ以降で利用可能。
不正なUTF-8文字列対策としてSRELLは次のことを行います。
error_utf8
をthrow
し、照合時はその時点で照合失敗とする。[*1]\u0030
は最短の表現である0x30
にのみマッチし、冗長な 0xc0 0xb0
, 0xe0 0x80 0xb0
, 0xf0 0x80 0x80 0xb0
にはマッチしない)。[*2]
basic_regex
のtypedef
のうちUnicode用のprefix (u8-
, u8c-
, u16-
, u16w-
, u1632w-
, u32-
, u32w-
) を持たぬ型は、入力文字列をUnicode値の連続として解釈します。
例えばCHAR_BIT
が8
である環境でsrell::regex
(srell::basic_regex<char>
) を使うと、入力文字列に現れる0x00
~0xFF
はそのままU+0000~U+00FFとして解釈されます。
UnicodeのU+0000~U+00FFはISO-8859-1と同じですので、結果的にsrell::regex
はISO-8859-1に対応しているとも言えます。
またsrell::regex
は、バイナリデータ中から特定のパターンを見つけ出すような場合にも使うことが出来ます。
srell::wregex
(srell::basic_regex<wchar_t>
) も同じで、文字列中の0x00
~WCHAR_MAX
までがそのまま同じ値のUnicode値として解釈されます。
WinAPIのW函数と組み合わせて使うのに適しているのは、UTF-16対応の srell::u16wregex
または srell::u1632wregex
です。srell::wregex
は事実上UCS-2対応となってしまいます。
Feature test macroが適切に定義されないコンパイラ用に、SRELLのversion 4.056までは次のようなマクロが利用可能でした。
SRELL_CPP11_CHAR1632_ENABLED |
|
---|---|
SRELL_CPP11_INITIALIZER_LIST_ENABLED |
Initializer listが使えるにもかかわらず |
SRELL_CPP11_MOVE_ENABLED |
Moveできるにもかかわらず |
SRELL_CPP20_CHAR8_ENABLED |
|
もしこれらSRELL_CPP*
マクロに相当するものが引き続き必要でしたら、対応する__cpp_*
マクロを直接定義してください。
C++11より前のコンパイラでは、wchar_t
が16ビット以上かつ21ビット未満の環境ならu8c-型とu16w-型とが、wchar_t
が21ビット以上ある環境ならu8c-型とu32w-型とがそれぞれ利用可能です。もっともそのような環境でも、SRELLをincludeする前に次のようなコードを書いておけば、u8c-型、u16-型、u32-型を3つとも利用することができます。
typedef unsigned short char16_t; // 16ビット以上ある符号無しの型をtypedefする。 typedef unsigned long char32_t; // 32ビット以上ある符号無しの型をtypedefする。 namespace std { typedef basic_string<char16_t> u16string; typedef basic_string<char32_t> u32string; } #define __cpp_unicode_characters // 手動でonにする。 // #define SRELL_CPP11_CHAR1632_ENABLED // SRELL 4.056まではこちら。
ちなみにUTF-8やUTF-16に固有の処理をしているのは、basic_regex
に対してテンプレート引数として渡されるu8regex_traits
、u16regex_traits
です。このことを利用して、たとえば basic_regex<uint32_t, u16regex_traits<uint32_t> >
のような「UTF-16文字列をuint32_t
型の配列で処理する型」なるものを作ることもできます。
C++規格がint型について保証している最低ビット幅は16です。SRELLをコーディングするにあたってはこのことを念頭に置いているつもりですが、実際にintが16 bitちょうどの環境でコンパイルできて、きちんとSRELLが動作するかどうかは不明です。
intやsize_tが32 bit以上あることを前提とすればコードを単純にできる箇所が複数ありますので、そう遠くないうちにint/size_tが32 bit未満の環境はコンパイルエラーとなるような変更を加えるかもしれません。
SRELLのソースコード中では文字列リテラルの使用を避けていますので、ASCIIと互換性のない文字コード、たとえばEBCDICベースの環境でも、SRELLのソースコードをEBCDICに変換すれば理論上は動くはずです。
ただしSRELLは常に入力文字列をUTF-8/16/32と見なして解釈しますので、EBCDICでエンコードされたソースコード中で、srell::regex re("a+");
のように書いても正常に動作しません。EBCDICの'a'
は0x81
で、'+'
は0x4e
ですので、先の正規表現はSRELLから見ると "\x81N"
相当に見えます。
ソースコードをEBCDICに変換したSRELLが期待通りに動くのは、正規表現文字列、検索対象文字列ともに外部ファイルから読み込むような場合のみです。
EBCDICのようなASCII非互換の環境を考慮しなくて良いのであれば、srell_updata3中の "\xHH"
エスケープが不要になり、同ファイルのサイズをコンパクトにできるというメリットがあります。今後SRELLのファイルサイズが気になってきたら(特に single-header/srell.hpp が1MBを超えた時には)、\xHHエスケープはやめてしまい、ASCIIと互換性のない文字コードで動くコンパイラには非対応ということにするかもしれません。
std::regexのregex_search(), regex_match()はbidirectional iterator以上のイテレータペアを検索対象文字列として引数に取ります。SRELLも初期よりこれに倣った仕様となっていますが、そうするために「bidirectional iteratorでしか使われない(random access iterator以上では使われない)Boyer Moore Horspool用コード」なるものが組み込まれています。
Bidirectional iteratorを使った検索というものにそれほど需要があるとも思えませんので、どこかのタイミングでSRELLが対応するイテレータを「random access iterator以上」に引き上げるかもしれません。
別ページに移しました。
以下は外部のサイトです。
(?imsx-imsx)
は除外され、(?imsx-imsx:subexpression)
形式のみの対応とすることに。
\Z
は除外して\A, \z
のみとすることに。
\p{Line_Terminator_sequence}
のような選択肢も考慮すべきとの声が出て現状維持。