Was ist eine nicht erfassende Gruppe in regulären Ausdrücken?


Antworten:


2327

Lassen Sie mich versuchen, dies anhand eines Beispiels zu erklären.

Betrachten Sie den folgenden Text:

http://stackoverflow.com/
/programming/tagged/regex

Nun, wenn ich den Regex unten darüber anwende ...

(https?|ftp)://([^/\r\n]+)(/[^\r\n]*)?

... Ich würde folgendes Ergebnis erhalten:

Match "http://stackoverflow.com/"
     Group 1: "http"
     Group 2: "stackoverflow.com"
     Group 3: "/"

Match "/programming/tagged/regex"
     Group 1: "https"
     Group 2: "stackoverflow.com"
     Group 3: "/questions/tagged/regex"

Das Protokoll ist mir jedoch egal - ich möchte nur den Host und den Pfad der URL. Daher ändere ich den regulären Ausdruck so, dass er die nicht erfassende Gruppe enthält (?:).

(?:https?|ftp)://([^/\r\n]+)(/[^\r\n]*)?

Nun sieht mein Ergebnis so aus:

Match "http://stackoverflow.com/"
     Group 1: "stackoverflow.com"
     Group 2: "/"

Match "/programming/tagged/regex"
     Group 1: "stackoverflow.com"
     Group 2: "/questions/tagged/regex"

Sehen? Die erste Gruppe wurde nicht erfasst. Der Parser verwendet es, um den Text abzugleichen, ignoriert ihn jedoch später im Endergebnis.


BEARBEITEN:

Lassen Sie mich auf Wunsch auch versuchen, Gruppen zu erklären.

Nun, Gruppen dienen vielen Zwecken. Sie können Ihnen helfen, genaue Informationen aus einer größeren Übereinstimmung zu extrahieren (die auch benannt werden kann), Sie können eine zuvor übereinstimmende Gruppe erneut abgleichen und sie können für Ersetzungen verwendet werden. Probieren wir einige Beispiele aus.

Stellen Sie sich vor, Sie haben eine Art XML oder HTML (beachten Sie, dass Regex möglicherweise nicht das beste Werkzeug für den Job ist , aber es ist ein gutes Beispiel). Sie möchten die Tags analysieren, damit Sie so etwas tun können (ich habe Leerzeichen hinzugefügt, um das Verständnis zu erleichtern):

   \<(?<TAG>.+?)\> [^<]*? \</\k<TAG>\>
or
   \<(.+?)\> [^<]*? \</\1\>

Der erste reguläre Ausdruck hat eine benannte Gruppe (TAG), während der zweite eine gemeinsame Gruppe verwendet. Beide regulären Ausdrücke machen dasselbe: Sie verwenden den Wert aus der ersten Gruppe (den Namen des Tags), um mit dem schließenden Tag übereinzustimmen. Der Unterschied besteht darin, dass der erste den Namen verwendet, um mit dem Wert übereinzustimmen, und der zweite den Gruppenindex verwendet (der bei 1 beginnt).

Versuchen wir jetzt einige Substitutionen. Betrachten Sie den folgenden Text:

Lorem ipsum dolor sit amet consectetuer feugiat fames malesuada pretium egestas.

Lassen Sie uns nun diesen dummen regulären Ausdruck darüber verwenden:

\b(\S)(\S)(\S)(\S*)\b

Diese Regex entspricht Wörtern mit mindestens 3 Zeichen und verwendet Gruppen, um die ersten drei Buchstaben zu trennen. Das Ergebnis ist folgendes:

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"
...

Wenn wir also die Substitutionszeichenfolge anwenden:

$1_$3$2_$4

... darüber versuchen wir, die erste Gruppe zu verwenden, einen Unterstrich hinzuzufügen, die dritte Gruppe zu verwenden, dann die zweite Gruppe, einen weiteren Unterstrich hinzuzufügen und dann die vierte Gruppe. Die resultierende Zeichenfolge wäre wie die folgende.

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.

Sie können benannte Gruppen auch für Ersetzungen verwenden, indem Sie ${name}.

Um mit Regexes herumzuspielen, empfehle ich http://regex101.com/ , das eine Reihe von Details zur Funktionsweise von Regex bietet. Es bietet auch einige Regex-Engines zur Auswahl.


3
@ajsie: Herkömmliche (Erfassungs-) Gruppen sind am nützlichsten, wenn Sie eine Ersetzungsoperation für die Ergebnisse durchführen. Hier ist ein Beispiel, in dem ich durch Kommas getrennte Nach-
Steve Wortham

2
Nein, es ist nicht dasselbe.
Ricardo Nolde

4
Könnte auch darauf hinweisen, dass nicht erfassende Gruppen einzigartig nützlich sind, wenn Regex als geteilte Trennzeichen verwendet werden: "Alice und Bob" -Split "\ s + (?: Und | oder) \ s +"
Jewgenij

7
Es wäre interessant, den Unterschied zwischen nicht erfassenden Gruppen (? :) und Lookahead- und Lookbehind-Behauptungen (? =,?!) Erklären zu lassen. Ich habe gerade angefangen, etwas über reguläre Ausdrücke zu lernen, aber nach meinem Verständnis werden nicht erfassende Gruppen zum Abgleichen verwendet und "zurückgegeben", was sie übereinstimmen, aber dieser "Rückgabewert" wird nicht zum Zurückverweisen "gespeichert". Lookahead- und Lookbehind-Behauptungen hingegen werden nicht nur nicht "gespeichert", sie sind auch nicht Teil einer Übereinstimmung, sie behaupten nur, dass etwas übereinstimmen würde, aber ihr "Übereinstimmungs" -Wert wird ignoriert, wenn ich mich nicht irre. (Habe ich ungefähr recht?)
Christian

5
[] Ist ein Satz; [123] stimmt einmal mit einem beliebigen Zeichen im Set überein; [^ 123] stimmt mit allem überein, was NICHT einmal im Set enthalten ist. [^ / \ r \ n] + entspricht einem oder mehreren Zeichen, die sich von /, \ r, \ n unterscheiden.
Ricardo Nolde

180

Sie können Erfassungsgruppen verwenden, um einen Ausdruck zu organisieren und zu analysieren. Eine nicht erfassende Gruppe hat den ersten Vorteil, aber nicht den Overhead der zweiten. Sie können beispielsweise weiterhin sagen, dass eine nicht erfassende Gruppe optional ist.

Angenommen, Sie möchten mit numerischem Text übereinstimmen, aber einige Zahlen könnten als 1., 2., 3., 4., ... geschrieben werden. Wenn Sie den numerischen Teil erfassen möchten, jedoch nicht das (optionale) Suffix, können Sie eine nicht erfassende Gruppe verwenden .

([0-9]+)(?:st|nd|rd|th)?

Das stimmt mit Zahlen in der Form 1, 2, 3 ... oder in der Form 1, 2, 3, ... überein, erfasst aber nur den numerischen Teil.


3
Prägnant und wahrscheinlich die beste Erklärung hier.
NelsonGon

107

?: wird verwendet, wenn Sie einen Ausdruck gruppieren möchten, ihn jedoch nicht als übereinstimmenden / erfassten Teil der Zeichenfolge speichern möchten.

Ein Beispiel wäre etwas, das mit einer IP-Adresse übereinstimmt:

/(?:\d{1,3}\.){3}\d{1,3}/

Beachten Sie, dass es mir egal ist, die ersten 3 Oktette zu speichern, aber die (?:...)Gruppierung ermöglicht es mir, den regulären Ausdruck zu verkürzen, ohne den Aufwand für das Erfassen und Speichern einer Übereinstimmung zu verursachen.


38

Dadurch wird die Gruppe nicht erfasst, was bedeutet, dass die von dieser Gruppe übereinstimmende Teilzeichenfolge nicht in die Liste der Erfassungen aufgenommen wird. Ein Beispiel in Rubin, um den Unterschied zu veranschaulichen:

"abc".match(/(.)(.)./).captures #=> ["a","b"]
"abc".match(/(?:.)(.)./).captures #=> ["b"]

Warum können wir hier nicht einfach "abc" .match (/.(.)./). Captures verwenden?
PRASANNA SARAF

@ PRASANNASARAF Das kannst du natürlich. Der Zweck des Codes bestand darin, zu zeigen, dass (?:)keine Erfassung erstellt wird, und kein nützliches Beispiel dafür zu demonstrieren (?:). (?:)ist nützlich, wenn Sie einen Unterausdruck gruppieren möchten (z. B. wenn Sie Quantifizierer auf einen nichtatomaren Unterausdruck anwenden möchten oder wenn Sie den Umfang von a einschränken möchten |), aber nichts erfassen möchten.
sepp2k

26

HISTORISCHE MOTIVATION:

Die Existenz nicht erfassender Gruppen kann mit Klammern erklärt werden.

Berücksichtigen Sie die Ausdrücke, (a|b)cund a|bcaufgrund der Priorität der Verkettung |stehen diese Ausdrücke für zwei verschiedene Sprachen ( {ac, bc}und{a, bc} jeweils).

Die Klammern werden jedoch auch als übereinstimmende Gruppe verwendet (wie in den anderen Antworten erläutert ...).

Wenn Sie eine Klammer haben möchten, aber den Unterausdruck nicht erfassen möchten, verwenden Sie NON-CAPTURING GROUPS. Im Beispiel(?:a|b)c


6
Ich habe mich gefragt warum. Wie ich denke, ist das "Warum" entscheidend, um diese Informationen auswendig zu lernen.
JMI MADISON

22

Lassen Sie mich dies anhand eines Beispiels versuchen:

Regex-Code: (?:animal)(?:=)(\w+)(,)\1\2

Suchbegriff:

Linie 1 - animal=cat,dog,cat,tiger,dog

Zeile 2 - animal=cat,cat,dog,dog,tiger

Zeile 3 - animal=dog,dog,cat,cat,tiger

(?:animal) -> Nicht erfasste Gruppe 1

(?:=)-> Nicht erfasste Gruppe 2

(\w+)-> Erfasste Gruppe 1

(,)-> Erfasste Gruppe 2

\1 -> Ergebnis der erfassten Gruppe 1, dh in Zeile 1 ist Katze, in Zeile 2 ist Katze, in Zeile 3 ist Hund.

\2 -> Ergebnis der erfassten Gruppe 2, dh Komma (,)

Also in diesem Code durch Geben \1und \2wir erinnern oder wiederholen das Ergebnis der erfassten Gruppe 1 bzw. 2 später im Code.

Gemäß der Reihenfolge des Codes (?:animal)sollte Gruppe 1 und (?:=)sollte Gruppe 2 sein und fährt fort ..

Aber indem ?:wir das geben , machen wir die Match-Gruppe nicht erfasst (die in der übereinstimmenden Gruppe nicht zählen, so dass die Gruppierungsnummer von der ersten erfassten Gruppe und nicht von der nicht erfassten Gruppe beginnt), so dass die Wiederholung des Ergebnisses der Match-Gruppe erfolgt (?:animal)kann später im Code nicht aufgerufen werden.

Hoffe, dies erklärt die Verwendung von nicht erfassenden Gruppen.

Geben Sie hier die Bildbeschreibung ein


14

Gruppen, die erfasst werden , können Sie später in der Regex verwenden, um sie abzugleichen, ODER Sie können sie im Ersatzteil der Regex verwenden. Wenn Sie eine nicht erfassende Gruppe erstellen, wird diese Gruppe aus einem dieser Gründe einfach von der Verwendung befreit.

Nicht erfassende Gruppen eignen sich hervorragend, wenn Sie versuchen, viele verschiedene Dinge zu erfassen, und es gibt einige Gruppen, die Sie nicht erfassen möchten.

Das ist so ziemlich der Grund, warum sie existieren. Während Sie etwas über Gruppen lernen, lernen Sie etwas über Atomgruppen , sie tun viel! Es gibt auch Lookaround-Gruppen, aber sie sind etwas komplexer und werden nicht so häufig verwendet.

Beispiel für die spätere Verwendung im regulären Ausdruck (Rückreferenz):

<([A-Z][A-Z0-9]*)\b[^>]*>.*?</\1> [Findet ein XML-Tag (ohne ns-Unterstützung)]

([A-Z][A-Z0-9]*) ist eine Erfassungsgruppe (in diesem Fall der Tagname)

Später in der Regex ist, \1was bedeutet, dass es nur mit dem gleichen Text übereinstimmt, der in der ersten Gruppe (der ([A-Z][A-Z0-9]*)Gruppe) war (in diesem Fall stimmt es mit dem End-Tag überein ).


Können Sie ein einfaches Beispiel geben, wie es später verwendet wird, um mit OR übereinzustimmen?
nie_had_a_name

Ich meine, Sie können verwenden, um später abzugleichen, oder Sie können es im Ersatz verwenden. Das oder in diesem Satz sollte nur zeigen, dass es zwei Verwendungszwecke für eine Erfassungsgruppe gibt
Bob Fincheimer

9

Nun, ich bin ein JavaScript-Entwickler und werde versuchen, seine Bedeutung für JavaScript zu erklären.

Stellen Sie sich ein Szenario vor, in dem Sie übereinstimmen möchten, cat is animal wenn Sie Katze und Tier zusammenbringen möchten und beide einen isdazwischen haben sollten.

 // this will ignore "is" as that's is what we want
"cat is animal".match(/(cat)(?: is )(animal)/) ;
result ["cat is animal", "cat", "animal"]

 // using lookahead pattern it will match only "cat" we can
 // use lookahead but the problem is we can not give anything
 // at the back of lookahead pattern
"cat is animal".match(/cat(?= is animal)/) ;
result ["cat"]

 //so I gave another grouping parenthesis for animal
 // in lookahead pattern to match animal as well
"cat is animal".match(/(cat)(?= is (animal))/) ;
result ["cat", "cat", "animal"]

 // we got extra cat in above example so removing another grouping
"cat is animal".match(/cat(?= is (animal))/) ;
result ["cat", "animal"]

7

In komplexen regulären Ausdrücken kann es vorkommen, dass Sie eine große Anzahl von Gruppen verwenden möchten, von denen einige für den Wiederholungsabgleich und einige für die Bereitstellung von Rückverweisen vorgesehen sind. Standardmäßig wird der zu jeder Gruppe passende Text in das Backreference-Array geladen. Wenn wir viele Gruppen haben und nur einige von ihnen aus dem Backreference-Array referenzieren müssen, können wir dieses Standardverhalten überschreiben, um dem regulären Ausdruck mitzuteilen, dass bestimmte Gruppen nur für die Wiederholungsbehandlung vorhanden sind und nicht erfasst und gespeichert werden müssen im Backreference-Array.


7

Ich kann die Top-Antworten nicht kommentieren, um Folgendes zu sagen: Ich möchte einen expliziten Punkt hinzufügen, der nur in den Top-Antworten enthalten ist:

Die Nicht-Erfassungsgruppe (?...) nicht entfernen nicht alle Zeichen aus dem ursprünglichen vollen Spiel, es nur reorganisiert den Regex optisch an den Programmierer.

Um auf einen bestimmten Teil der Regex ohne definierte Fremdzeichen zuzugreifen, müssten Sie immer verwenden .group(<index>)


2
Sie haben den wichtigsten Hinweis gegeben, der in den restlichen Antworten fehlte. Ich habe alle Beispiele in ihnen ausprobiert und die besten Erklärungen verwendet, da ich nicht das gewünschte Ergebnis erzielt habe. Nur Ihr Beitrag hat mir gezeigt, wo ich falsch gelaufen bin.
Seshadri R

Froh das zu hören!
Scott Anderson

6

tl; dr nicht-einfangende Gruppen, wie der Name schon sagt sind die Teile der Regex , dass Sie nicht möchten , dass in dem Spiel enthalten sein und ?:sind eine Möglichkeit , eine Gruppe als nicht-Capturing zu definieren.

Angenommen, Sie haben eine E-Mail-Adresse example@example.com. Der folgende reguläre Ausdruck erstellt zwei Gruppen , den ID-Teil und den @ example.com-Teil. (\p{Alpha}*[a-z])(@example.com). Der Einfachheit halber extrahieren wir den gesamten Domainnamen einschließlich des @Zeichens.

Angenommen, Sie benötigen nur den ID-Teil der Adresse. Was Sie tun möchten, ist, die erste Gruppe des Match-Ergebnisses zu ()erfassen , die von der Regex umgeben ist, und dies zu tun , indem Sie die nicht erfassende Gruppensyntax verwenden, d ?:. H. Der reguläre Ausdruck (\p{Alpha}*[a-z])(?:@example.com)gibt also nur den ID-Teil der E-Mail zurück.


5

Eine interessante Sache, auf die ich gestoßen bin, ist die Tatsache, dass Sie eine Erfassungsgruppe innerhalb einer nicht erfassenden Gruppe haben können. Schauen Sie sich unten den regulären Ausdruck für passende Web-URLs an:

var parse_url_regex = /^(?:([A-Za-z]+):)(\/{0,3})([0-9.\-A-Za-z]+)(?::(\d+))?(?:\/([^?#]*))?(?:\?([^#]*))?(?:#(.*))?$/;

Eingabe-URL-Zeichenfolge:

var url = "http://www.ora.com:80/goodparts?q#fragment";

Die erste Gruppe in meiner regex (?:([A-Za-z]+):)ist eine Nicht-Erfassung Gruppe , die das Protokollschema und Doppelpunkt entspricht :Charakter dh http:aber wenn ich unten Code ausgeführt wurde, war ich den ersten Index des zurückgegebenen Arrays zu sehen , wurde die Zeichenfolge enthält , httpals ich dachte , dass httpund Kolon :Beide werden nicht gemeldet, da sie sich in einer nicht erfassenden Gruppe befinden.

console.debug(parse_url_regex.exec(url));

Geben Sie hier die Bildbeschreibung ein

Ich dachte, wenn die erste Gruppe (?:([A-Za-z]+):)eine nicht erfassende Gruppe ist, warum gibt sie dann eine httpZeichenfolge im Ausgabearray zurück?

Wenn Sie also feststellen, dass sich ([A-Za-z]+)innerhalb der nicht erfassenden Gruppe eine verschachtelte Gruppe befindet. Diese verschachtelte Gruppe ([A-Za-z]+)ist eine Erfassungsgruppe (die ?:am Anfang keine hat ) innerhalb einer nicht erfassenden Gruppe (?:([A-Za-z]+):). Aus diesem Grund wird der Text httpimmer noch erfasst, aber das Doppelpunktzeichen :, das sich innerhalb der nicht erfassenden Gruppe, aber außerhalb der Erfassungsgruppe befindet, wird nicht im Ausgabearray gemeldet.


2

Öffnen Sie Ihre Google Chrome devTools und dann die Registerkarte Konsole: und geben Sie Folgendes ein:

"Peace".match(/(\w)(\w)(\w)/)

Führen Sie es aus und Sie werden sehen:

["Pea", "P", "e", "a", index: 0, input: "Peace", groups: undefined]

Die JavaScriptRegExp-Engine erfasst drei Gruppen, die Elemente mit den Indizes 1,2,3. Verwenden Sie nun die nicht erfassende Markierung, um das Ergebnis anzuzeigen.

"Peace".match(/(?:\w)(\w)(\w)/)

Das Ergebnis ist:

["Pea", "e", "a", index: 0, input: "Peace", groups: undefined]

Dies ist offensichtlich, was eine nicht erfassende Gruppe ist.


2

Ich denke, ich würde dir die Antwort geben. Verwenden Sie keine Erfassungsvariablen, ohne zu überprüfen, ob die Übereinstimmung erfolgreich war.

Die Erfassungsvariablen $1usw. sind nur gültig, wenn die Übereinstimmung erfolgreich war, und sie werden auch nicht gelöscht.

#!/usr/bin/perl  
use warnings;
use strict;   
$_ = "bronto saurus burger";
if (/(?:bronto)? saurus (steak|burger)/)
{
    print "Fred wants a  $1";
}
else
{
    print "Fred dont wants a $1 $2";
}

In dem obigen Beispiel zu vermeiden bronto bei der Erfassung $1, (?:)verwendet wird.

Wenn das Muster übereinstimmt, $1wird es als nächstes gruppiertes Muster erfasst.

Die Ausgabe wird also wie folgt sein:

Fred wants a burger

Es ist nützlich, wenn Sie nicht möchten, dass die Übereinstimmungen gespeichert werden.


1

Es ist extrem einfach. Wir können anhand eines einfachen Datumsbeispiels verstehen, dass wenn das Datum als 1. Januar 2019 oder 2. Mai 2019 oder ein anderes Datum angegeben wird und wir es einfach in das Format TT / MM / JJJJ konvertieren möchten, würden wir die Monatsdaten nicht benötigen Name, der für diese Angelegenheit Januar oder Februar ist. Um also den numerischen Teil, aber nicht das (optionale) Suffix zu erfassen, können Sie eine nicht erfassende Gruppe verwenden.

so wäre der reguläre Ausdruck,

([0-9]+)(?:January|February)?

So einfach ist das.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.