サーチ…
前書き
正規表現 (regexsまたはregexpsとも呼ばれる)は、操作される文字列で一致するパターンを表すテキスト構文です。
c ++ 11で導入された正規表現は、マッチした文字列のリターン配列や、操作された文字列のマッチしたパターンをどのように置き換えるかを定義する別のテキスト構文をオプションでサポートします。
構文
- regex_match //文字シーケンス全体が正規表現にマッチしたかどうかを返します。オプションでマッチオブジェクトに取り込みます
- regex_search //文字列の一部が正規表現とマッチしたかどうかを返します。オプションでマッチオブジェクトに取り込みます
- regex_replace //置換文字列を介して正規表現によって変更された入力文字列を返します。
- regex_token_iterator //イテレータによって定義された文字シーケンス、反復処理するキャプチャインデックスのリスト、および正規表現で初期化されます。 Dereferencingは、正規表現の現在インデックスされている一致を返します。インクリメントは次のキャプチャインデックスに移動するか、または現在のインデックスが最後のインデックスにある場合は、インデックスをリセットし、文字シーケンス内の正規表現マッチの次の出現を後にします
- regex_iterator //イテレータと正規表現で定義された文字シーケンスで初期化されます。逆参照は、正規表現全体が現在一致している文字シーケンスの部分を返します。インクリメントすると、文字シーケンス中の正規表現マッチの次の出現を見つける
パラメーター
署名 | 説明 |
---|---|
bool regex_match(BidirectionalIterator first, BidirectionalIterator last, smatch& sm, const regex& re, regex_constraints::match_flag_type flags) | BidirectionalIterator インクリメント提供する任意の文字イテレータであるsmatch かもしれcmatch または任意の他の他の変異体match_results の種類受け入れBidirectionalIterator smatch 正規表現の結果を返しますが必要ない場合か、引数が省略されてre 全体の文字に一致しfirst とlast 定義されたシーケンス |
bool regex_match(const string& str, smatch& sm, const regex re&, regex_constraints::match_flag_type flags) | string いずれであってもよいconst char* あるいはL値string 、R-値受付機能string 明示的に削除され smatch あってもよいcmatch 又は他の任意の他の変異体match_results の種類受け付けstr smatch 引数があれば省略されてもよいしかどうかを正規表現の結果は、 戻り値を必要とされていないre によって定義された全体の文字列と一致するstr |
基本的なregex_matchとregex_searchの例
const auto input = "Some people, when confronted with a problem, think \"I know, I'll use regular expressions.\""s;
smatch sm;
cout << input << endl;
// If input ends in a quotation that contains a word that begins with "reg" and another word begining with "ex" then capture the preceeding portion of input
if (regex_match(input, sm, regex("(.*)\".*\\breg.*\\bex.*\"\\s*$"))) {
const auto capture = sm[1].str();
cout << '\t' << capture << endl; // Outputs: "\tSome people, when confronted with a problem, think\n"
// Search our capture for "a problem" or "# problems"
if(regex_search(capture, sm, regex("(a|d+)\\s+problems?"))) {
const auto count = sm[1] == "a"s ? 1 : stoi(sm[1]);
cout << '\t' << count << (count > 1 ? " problems\n" : " problem\n"); // Outputs: "\t1 problem\n"
cout << "Now they have " << count + 1 << " problems.\n"; // Ouputs: "Now they have 2 problems\n"
}
}
regex_replaceの例
このコードはさまざまなブレーススタイルを取り入れ、それらを1つの真のブレーススタイルに変換します。
const auto input = "if (KnR)\n\tfoo();\nif (spaces) {\n foo();\n}\nif (allman)\n{\n\tfoo();\n}\nif (horstmann)\n{\tfoo();\n}\nif (pico)\n{\tfoo(); }\nif (whitesmiths)\n\t{\n\tfoo();\n\t}\n"s;
cout << input << regex_replace(input, regex("(.+?)\\s*\\{?\\s*(.+?;)\\s*\\}?\\s*"), "$1 {\n\t$2\n}\n") << endl;
regex_token_iteratorの例
std::regex_token_iterator
は、Comma Separated Valueファイルの要素を抽出するためのすばらしいツールです 。反復の利点とは別に、このイテレータは、他のメソッドが苦労しているエスケープされたカンマを取得することもできます。
const auto input = "please split,this,csv, ,line,\\,\n"s;
const regex re{ "((?:[^\\\\,]|\\\\.)+)(?:,|$)" };
const vector<string> m_vecFields{ sregex_token_iterator(cbegin(input), cend(input), re, 1), sregex_token_iterator() };
cout << input << endl;
copy(cbegin(m_vecFields), cend(m_vecFields), ostream_iterator<string>(cout, "\n"));
正規表現のイテレータで注目すべき点は、 regex
引数はL値でなければならないということです。 R値は機能しません 。
regex_iteratorの例
キャプチャの処理を繰り返し実行するregex_iterator
があるregex_iterator
は、 regex_iterator
が良い選択です。間接参照regex_iterator
返しmatch_result
。これは、相互依存関係を持つ条件付きキャプチャまたはキャプチャに最適です。 C ++コードをトークン化したいとしましょう。与えられた:
enum TOKENS {
NUMBER,
ADDITION,
SUBTRACTION,
MULTIPLICATION,
DIVISION,
EQUALITY,
OPEN_PARENTHESIS,
CLOSE_PARENTHESIS
};
この文字列を次のようにregex_iterator
const auto input = "42/2 + -8\t=\n(2 + 2) * 2 * 2 -3"s
vector<TOKENS> tokens;
const regex re{ "\\s*(\\(?)\\s*(-?\\s*\\d+)\\s*(\\)?)\\s*(?:(\\+)|(-)|(\\*)|(/)|(=))" };
for_each(sregex_iterator(cbegin(input), cend(input), re), sregex_iterator(), [&](const auto& i) {
if(i[1].length() > 0) {
tokens.push_back(OPEN_PARENTHESIS);
}
tokens.push_back(i[2].str().front() == '-' ? NEGATIVE_NUMBER : NON_NEGATIVE_NUMBER);
if(i[3].length() > 0) {
tokens.push_back(CLOSE_PARENTHESIS);
}
auto it = next(cbegin(i), 4);
for(int result = ADDITION; it != cend(i); ++result, ++it) {
if (it->length() > 0U) {
tokens.push_back(static_cast<TOKENS>(result));
break;
}
}
});
match_results<string::const_reverse_iterator> sm;
if(regex_search(crbegin(input), crend(input), sm, regex{ tokens.back() == SUBTRACTION ? "^\\s*\\d+\\s*-\\s*(-?)" : "^\\s*\\d+\\s*(-?)" })) {
tokens.push_back(sm[1].length() == 0 ? NON_NEGATIVE_NUMBER : NEGATIVE_NUMBER);
}
正規表現のイテレータで注目すべき点は、 regex
引数はL値でなければならず、R値は機能しないということです。Visual Studio regex_iterator Bug?
文字列を分割する
std::vector<std::string> split(const std::string &str, std::string regex)
{
std::regex r{ regex };
std::sregex_token_iterator start{ str.begin(), str.end(), r, -1 }, end;
return std::vector<std::string>(start, end);
}
split("Some string\t with whitespace ", "\\s+"); // "Some", "string", "with", "whitespace"
数量化
検証する電話番号としてconst string input
が与えられたとしましょう。最初に、 0以上の量子を持つ数値入力を要求することができます: regex_match(input, regex("\\d*"))
または1つ以上の量子 : regex_match(input, regex("\\d+"))
場合は、それらの両方は、本当に短い秋input
「123」我々は、少なくとも7桁の数字を取得していることを保証するために、n個以上の数量詞を使用してみましょう:のような無効な数値文字列が含まれています:
regex_match(input, regex("\\d{7,}"))
これにより、少なくとも数字の電話番号を取得することが保証されinput
が、 input
は「123456789012」のように長すぎる数字の文字列を含めることもできます。そこそこnとmの数量詞の間に行くことができますinput
、少なくとも7桁の数字ではなく、11以上です。
regex_match(input, regex("\\d{7,11}"));
これは近い私たちを取得しますが、[7]、[11]の範囲内にある違法な数値文字列はまだのように、受け入れられている:「123456789は、」それでは、 怠惰な数量詞と国コードをオプションにしてみましょう:
regex_match(input, regex("\\d?\\d{7,10}"))
それは怠惰な数量詞は 、 できるだけ少ない文字と一致していることに注意することが重要だことで一致した、すでに10文字がある場合にマッチします。この文字がある唯一の方法ので\d{7,10}
。最初の文字を貪欲に照合するためには、 \d{0,1}
を使用しなければなりませんでした。) 遅延量子は、他のすべての量子に追加できます。
ここでは、市外局番をオプションにして、市外局番があれば国番号を受け入れますか?
regex_match(input, regex("(?:\\d{3,4})?\\d{7}"))
この最終正規表現では\d{7}
に 7桁が必要です。これらの7桁には、3桁または4桁のいずれかを前に付けることもできます。
怠惰な数量子を追加しなかったことに注意してください: 、 \d{3,4}?\d{7}
\d{3,4}?
3文字または4文字のいずれかと一致し、3を好んでいました。代わりに、非捕捉グループを最大で1回一致させ、一致しないことを好みます。 input
「1234567」のような市外局番が含まれていないと、不一致が発生しinput
。
量指定子の結論では、使用できる追加量指定子、 所有量限定子について言及したいと思います。 lazy量指定子または所有量 指定子の いずれかを任意の量子に追加できます。 所有量限定子の唯一の機能は、正規表現エンジンに助言することです。貪欲にこれらの文字を取り、正規表現が失敗してもそれをあきらめないようにします。これはあまり意味がありません: regex_match(input, regex("\\d{3,4}+\\d{7}))
" 1234567890 "のようなinput
は\d{3,4}+
一致する3が正規表現を成功させたとしても、 \d{3,4}+
は常に4文字と一致します。
所有量指定子は、定量化されたトークンが一致可能な文字数を制限するときに最もよく使用されます 。例えば:
regex_match(input, regex("(?:.*\\d{3,4}+){3}"))
input
に以下のいずれかが含まれているかどうかを照合するために使用できます。
123 456 7890
123-456-7890
(123)456-7890
(123)456 - 7890
しかし、この正規表現が本当に輝いているのは、 input
に不正な入力が含まれている場合です。
12345 - 67890
所有量限定子がなければ、regexエンジンは元に戻り、一致する組み合わせを見つけることができるかどうかを調べるために.*
と3または4文字のすべての組み合わせをテストしなければなりません。 所有量限定子で正規表現は2 番目の 所有量限定子が '0'文字のままで始まるところから始まり、正規表現エンジンは\d{3,4}
が一致するように.*
を調整しようとします。正規表現が失敗することができないときは、前の.*
調整がマッチを許可したかどうかを確認するためにバックトラッキングは行われません。
アンカー
C ++にはアンカーが4つしかありません:
-
^
は文字列の先頭をアサートします - 文字列の終わりを示す
$
-
\b
は文字列の先頭または末尾に\W
文字をアサートします -
\B
は\w
文字をアサートします
のは、例えば、我々はそれの記号で番号をキャプチャしたいとしましょう:
auto input = "+1--12*123/+1234"s;
smatch sm;
if(regex_search(input, sm, regex{ "(?:^|\\b\\W)([+-]?\\d+)" })) {
do {
cout << sm[1] << endl;
input = sm.suffix().str();
} while(regex_search(input, sm, regex{ "(?:^\\W|\\b\\W)([+-]?\\d+)" }));
}
ここで重要なのは、アンカーが文字を消費しないことです。