非キャプチャグループ、つまり、(?:)
は正規表現でどのように使用され、それらは何に適していますか?
18 に答える
例を挙げて説明してみましょう。
次のテキストを検討してください。
http://stackoverflow.com/
https://stackoverflow.com/questions/tagged/regex
ここで、以下の正規表現を適用すると...
(https?|ftp)://([^/\r\n]+)(/[^\r\n]*)?
...次の結果が得られます。
Match "http://stackoverflow.com/"
Group 1: "http"
Group 2: "stackoverflow.com"
Group 3: "/"
Match "https://stackoverflow.com/questions/tagged/regex"
Group 1: "https"
Group 2: "stackoverflow.com"
Group 3: "/questions/tagged/regex"
しかし、プロトコルは気にしません。必要なのは、URL のホストとパスだけです。そのため、正規表現を変更して、非キャプチャ グループを含めます(?:)
。
(?:https?|ftp)://([^/\r\n]+)(/[^\r\n]*)?
今、私の結果は次のようになります。
Match "http://stackoverflow.com/"
Group 1: "stackoverflow.com"
Group 2: "/"
Match "https://stackoverflow.com/questions/tagged/regex"
Group 1: "stackoverflow.com"
Group 2: "/questions/tagged/regex"
見る?最初のグループはキャプチャされていません。パーサーはそれを使用してテキストを照合しますが、後で最終結果で無視します。
編集:
リクエストに応じて、グループについても説明してみましょう。
グループには多くの目的があります。それらは、より大きな一致 (名前を付けることもできます) から正確な情報を抽出するのに役立ち、以前に一致したグループを再一致させ、置換に使用できます。いくつかの例を試してみましょう。
ある種の XML または HTML があると想像してください (正規表現は仕事に最適なツールではないかもしれませんが、例としては良いことに注意してください)。タグを解析したいので、次のようにすることができます (わかりやすくするためにスペースを追加しました)。
\<(?<TAG>.+?)\> [^<]*? \</\k<TAG>\>
or
\<(.+?)\> [^<]*? \</\1\>
最初の正規表現には名前付きグループ (TAG) があり、2 番目の正規表現は共通グループを使用します。どちらの正規表現も同じことを行います。最初のグループの値 (タグの名前) を使用して、終了タグと一致させます。違いは、最初のものは名前を使用して値を一致させ、2 つ目はグループ インデックス (1 から始まる) を使用することです。
では、いくつかの置換を試してみましょう。次のテキストを検討してください。
Lorem ipsum dolor sit amet consectetuer feugiat fames malesuada pretium egestas.
それでは、このばかげた正規表現を使用してみましょう。
\b(\S)(\S)(\S)(\S*)\b
この正規表現は、3 文字以上の単語に一致し、グループを使用して最初の 3 文字を区切ります。結果は次のとおりです。
Match "Lorem"
Group 1: "L"
Group 2: "o"
Group 3: "r"
Group 4: "em"
Match "ipsum"
Group 1: "i"
Group 2: "p"
Group 3: "s"
Group 4: "um"
...
Match "consectetuer"
Group 1: "c"
Group 2: "o"
Group 3: "n"
Group 4: "sectetuer"
...
したがって、置換文字列を適用すると、次のようになります。
$1_$3$2_$4
... その上で、最初のグループを使用し、アンダースコアを追加し、3 番目のグループを使用し、次に 2 番目のグループを使用し、別のアンダースコアを追加し、次に 4 番目のグループを使用しようとしています。結果の文字列は次のようになります。
L_ro_em i_sp_um d_lo_or s_ti_ a_em_t c_no_sectetuer f_ue_giat f_ma_es m_la_esuada p_er_tium e_eg_stas.
を使用して、置換に名前付きグループを使用することもできます${name}
。
正規表現をいじるには、 http://regex101.com/をお勧めします。ここには、正規表現がどのように機能するかについてかなりの詳細が記載されています。また、いくつかの正規表現エンジンから選択できます。
キャプチャ グループを使用して、式を整理および解析できます。非キャプチャ グループには最初の利点がありますが、2 番目のオーバーヘッドはありません。たとえば、非キャプチャ グループはオプションであると言うことができます。
数値テキストに一致させたいとしますが、一部の数値は 1 番目、2 番目、3 番目、4 番目、... のように記述できます。(オプションの) 接尾辞ではなく数値部分をキャプチャする場合は、非キャプチャ グループを使用できます。 .
([0-9]+)(?:st|nd|rd|th)?
これは、1、2、3... または 1st、2nd、3rd、... の形式の数値に一致しますが、数値部分のみをキャプチャします。
?:
式をグループ化したいが、文字列の一致/キャプチャされた部分として保存したくない場合に使用されます。
例は、IP アドレスに一致するものです。
/(?:\d{1,3}\.){3}\d{1,3}/
最初の 3 オクテットを保存することは気にしないことに注意してください。ただし、(?:...)
グループ化により、一致のキャプチャと保存のオーバーヘッドを招くことなく、正規表現を短縮できます。
歴史的動機:
非捕捉グループの存在は、括弧を使用して説明できます。
式(a|b)c
anda|bc
を考えてみましょう|
。これらの式は 2 つの異なる言語 ({ac, bc}
および{a, bc}
それぞれ) を表します。
ただし、括弧は一致するグループとしても使用されます(他の回答で説明されているように...)。
括弧を付けたいが部分式をキャプチャしたくない場合は、NON-CAPTURING GROUPS を使用します。例では、(?:a|b)c
グループを非キャプチャにします。つまり、そのグループに一致する部分文字列はキャプチャのリストに含まれません。違いを説明するためのルビーの例:
"abc".match(/(.)(.)./).captures #=> ["a","b"]
"abc".match(/(?:.)(.)./).captures #=> ["b"]
キャプチャしたグループは、後で正規表現で使用して一致させるか、正規表現の置換部分で使用できます。非キャプチャグループを作成すると、これらの理由のいずれかでそのグループが使用されなくなります。
非キャプチャ グループは、さまざまなものをキャプチャしようとしていて、キャプチャしたくないグループがいくつかある場合に最適です。
それが彼らが存在するほとんどの理由です。グループについて学んでいる間に、Atomic Groupsについて学んでください。ルックアラウンド グループもありますが、もう少し複雑であまり使用されません。
後で正規表現 (後方参照) で使用する例:
<([A-Z][A-Z0-9]*)\b[^>]*>.*?</\1>
[ xml タグを検索します (ns サポートなし) ]
([A-Z][A-Z0-9]*)
キャプチャ グループです (この場合はタグ名です)。
正規表現の後半では\1
、最初のグループ (グループ) にあった同じテキストにのみ一致することを意味します([A-Z][A-Z0-9]*)
(この場合、終了タグに一致しています)。
tl;dr非キャプチャ グループは、その名前が示すように、一致に含めたくない正規表現の部分であり?:
、グループを非キャプチャとして定義する方法です。
あなたがメールアドレスを持っているとしましょうexample@example.com
。次の正規表現は、id 部分と @example.com 部分の2 つのグループを作成します。(\p{Alpha}*[a-z])(@example.com)
. @
簡単にするために、文字を含むドメイン名全体を抽出しています。
ここで、アドレスの id 部分だけが必要だとしましょう。やりたいことは()
、正規表現で囲まれた一致結果の最初のグループを取得することです。これを行う方法は、非キャプチャ グループ構文、つまり?:
. したがって、正規表現(\p{Alpha}*[a-z])(?:@example.com)
はメールの ID 部分のみを返します。
複雑な正規表現では、多数のグループを使用したい状況が発生する可能性があります。その一部は繰り返しの一致に使用され、一部は後方参照を提供するために使用されます。デフォルトでは、各グループに一致するテキストが後方参照配列にロードされます。多くのグループがあり、後方参照配列からそれらの一部のみを参照できるようにする必要がある場合、このデフォルトの動作をオーバーライドして、特定のグループが繰り返し処理のためだけに存在し、キャプチャして保存する必要がないことを正規表現に伝えることができます後方参照配列で。
簡単な答え
それらを使用して、いくつかの可能性のいずれかがここ(?:one|two)
またはオプションのフレーズcamp(?:site)?
、または一般的に、グループ/フレーズ/セクションを特に参照する必要なく参照したい場所で確実に発生するようにします。
キャプチャされたグループの数を最小限に抑えます。
地理座標の例を見てみましょう。以下は 2 つのグループに一致します
Latitude,Longitude
([+-]?\d+(?:\.\d+)?),([+-]?\d+(?:\.\d+)?)
1つ取りましょう([+-]?\d+(?:\.\d+)?)
co-ordinate は整数のようにすることも、次のようにすること58
もでき58.666
ます。.666
(\.\d+)?
(...)? - for optional
しかし、それは別のグループの試合になります。58
との 2 つの一致.666
は必要ありません。一致として単一の緯度が必要です。ここに非捕獲グループが来る(?:)
non-capturing group[+-]?\d+(?:\.\d+)?
では、58.666 と 58 の両方が単一の一致です