Che cos'è un gruppo che non cattura le espressioni regolari?


Risposte:


2329

Vorrei provare a spiegarlo con un esempio.

Considera il seguente testo:

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

Ora, se applico il regex qui sotto su di esso ...

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

... Vorrei ottenere il seguente risultato:

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"

Ma non mi interessa il protocollo: voglio solo l'host e il percorso dell'URL. Quindi, cambio regex per includere il gruppo non acquisente (?:).

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

Ora, il mio risultato è simile al seguente:

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

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

Vedere? Il primo gruppo non è stato catturato. Il parser lo usa per abbinare il testo, ma lo ignora in seguito, nel risultato finale.


MODIFICARE:

Come richiesto, lasciami provare a spiegare anche i gruppi.

Bene, i gruppi hanno molti scopi. Possono aiutarti a estrarre informazioni esatte da una corrispondenza più grande (che può anche essere denominata), ti consentono di rivalutare un gruppo precedente abbinato e possono essere utilizzate per le sostituzioni. Facciamo alcuni esempi, vero?

Immagina di avere una sorta di XML o HTML ( tieni presente che regex potrebbe non essere lo strumento migliore per il lavoro , ma è bello come esempio). Vuoi analizzare i tag, quindi potresti fare qualcosa del genere (ho aggiunto spazi per renderlo più facile da capire):

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

Il primo regex ha un gruppo denominato (TAG), mentre il secondo utilizza un gruppo comune. Entrambi i regex fanno la stessa cosa: usano il valore del primo gruppo (il nome del tag) per abbinare il tag di chiusura. La differenza è che il primo utilizza il nome per abbinare il valore e il secondo utilizza l'indice di gruppo (che inizia da 1).

Proviamo alcune sostituzioni ora. Considera il seguente testo:

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

Ora, usiamo questa stupida regex su di essa:

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

Questa regex abbina le parole con almeno 3 caratteri e usa i gruppi per separare le prime tre lettere. Il risultato è questo:

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

Quindi, se applichiamo la stringa di sostituzione:

$1_$3$2_$4

... su di esso, stiamo cercando di utilizzare il primo gruppo, aggiungere un carattere di sottolineatura, utilizzare il terzo gruppo, quindi il secondo gruppo, aggiungere un altro carattere di sottolineatura e quindi il quarto gruppo. La stringa risultante sarebbe come quella qui sotto.

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.

Puoi usare i gruppi con nome anche per le sostituzioni, usando ${name}.

Per giocare con regex, raccomando http://regex101.com/ , che offre una buona quantità di dettagli su come funziona il regex; offre anche alcuni motori regex tra cui scegliere.


3
@ajsie: i gruppi tradizionali (di acquisizione) sono molto utili se si sta eseguendo un'operazione di sostituzione sui risultati. Ecco un esempio in cui sto prendendo i cognomi e i nomi separati da virgola e quindi invertendo il loro ordine (grazie ai gruppi denominati) ... regexhero.net/tester/?id=16892996-64d4-4f10-860a-24f28dad7e30
Steve Wortham

2
No, non è lo stesso.
Ricardo Nolde,

4
Potresti anche sottolineare che i gruppi non catturanti sono unicamente utili quando si usa regex come delimitatori divisi: "Alice e Bob" -split "\ s + (?: and | or) \ s +"
Yevgeniy

7
Sarebbe interessante avere la differenza tra i gruppi non catturanti (? :) e le asserzioni lookahead e lookbehind (? =,?!) Spiegate. Ho appena iniziato a conoscere le espressioni regolari, ma da quanto ho capito, i gruppi non acquisiti vengono utilizzati per la corrispondenza e "restituiscono" ciò che corrispondono, ma quel "valore restituito" non è "memorizzato" per il riferimento indietro. Le asserzioni lookahead e lookbehind non sono solo non "memorizzate", ma non fanno parte di una corrispondenza, affermano solo che qualcosa corrisponderebbe, ma il loro valore "match" viene ignorato, se non sbaglio .. (Ho approssimativamente ragione?)
Christian,

5
[] è un set; [123] corrisponde a qualsiasi carattere all'interno del set una volta; [^ 123] corrisponde a una volta NON all'interno del set; [^ / \ r \ n] + corrisponde a uno o più caratteri diversi da /, \ r, \ n.
Ricardo Nolde,

180

È possibile utilizzare i gruppi di acquisizione per organizzare e analizzare un'espressione. Un gruppo senza acquisizione ha il primo vantaggio, ma non ha il sovraccarico del secondo. Puoi ancora dire che un gruppo non acquisito è facoltativo, ad esempio.

Supponiamo che tu voglia abbinare il testo numerico, ma alcuni numeri potrebbero essere scritti come 1 °, 2 °, 3 °, 4 °, ... Se vuoi catturare la parte numerica, ma non il suffisso (facoltativo) puoi usare un gruppo non di acquisizione .

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

Che corrisponderà ai numeri nella forma 1, 2, 3 ... o nella forma 1 °, 2 °, 3 °, ... ma catturerà solo la parte numerica.


3
Concisa e probabilmente la migliore spiegazione qui.
Nelson,

107

?: viene utilizzato quando si desidera raggruppare un'espressione, ma non si desidera salvarla come parte della stringa corrispondente / acquisita.

Un esempio potrebbe essere qualcosa che corrisponda a un indirizzo IP:

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

Nota che non mi interessa salvare i primi 3 ottetti, ma il (?:...)raggruppamento mi permette di accorciare la regex senza incorrere nel sovraccarico di catturare e memorizzare una partita.


38

Rende il gruppo non acquisibile, il che significa che la sottostringa corrispondente a quel gruppo non verrà inclusa nell'elenco delle acquisizioni. Un esempio in rubino per illustrare la differenza:

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

Perché non possiamo semplicemente usare "abc" .match (/.(.)./). Acquisizioni qui?
PRASANNA SARAF,

@PRASANNASARAF Puoi, ovviamente. Il punto del codice era mostrare che (?:)non produce una cattura, non dimostrare un utile esempio di (?:). (?:)è utile quando si desidera raggruppare una sottoespressione (ad esempio quando si desidera applicare quantificatori a una sottoespressione non atomica o se si desidera limitare l'ambito di a |), ma non si desidera acquisire nulla.
sepp2k,

26

MOTIVAZIONE STORICA:

L'esistenza di gruppi non catturanti può essere spiegata con l'uso della parentesi.

Considera le espressioni (a|b)ce a|bc, a causa della priorità della concatenazione rispetto a |, queste espressioni rappresentano due lingue diverse ( {ac, bc}e{a, bc} rispettivamente).

Tuttavia, le parentesi vengono utilizzate anche come gruppo corrispondente (come spiegato dalle altre risposte ...).

Quando vuoi avere una parentesi ma non catturare la sottoespressione usi GRUPPI NON CATTURANTI. Nell'esempio,(?:a|b)c


6
Mi chiedevo perché. A mio avviso, il "perché" è fondamentale per memorizzare queste informazioni.
JMI MADISON,

22

Vorrei provare questo con un esempio:

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

Stringa di ricerca:

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

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

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

(?:animal) -> Gruppo non acquisito 1

(?:=)-> Gruppo non acquisito 2

(\w+)-> Gruppo acquisito 1

(,)-> Gruppo acquisito 2

\1 -> risultato del gruppo acquisito 1, ovvero nella riga 1 è gatto, nella riga 2 è gatto, nella riga 3 è cane.

\2 -> risultato del gruppo acquisito 2 ovvero virgola (,)

Quindi in questo codice dando \1e \2ricordiamo o ripetiamo il risultato del gruppo catturato 1 e 2 rispettivamente più tardi nel codice.

Secondo l'ordine del codice (?:animal)dovrebbe essere il gruppo 1 e (?:=)dovrebbe essere il gruppo 2 e continua ..

ma dando il ?:rendiamo il gruppo di match non catturato (che non conta nel gruppo corrispondente, quindi il numero di raggruppamento inizia dal primo gruppo catturato e non da quello non catturato), in modo che la ripetizione del risultato del gruppo di match (?:animal)non può essere chiamato in seguito nel codice.

Spero che questo spieghi l'uso di un gruppo che non cattura.

inserisci qui la descrizione dell'immagine


14

I gruppi che acquisiscono possono essere utilizzati in seguito in regex per abbinarli OPPURE è possibile utilizzarli nella parte sostitutiva di regex. La creazione di un gruppo non di acquisizione esonera semplicemente quel gruppo dall'essere utilizzato per uno di questi motivi.

I gruppi che non catturano sono grandi se stai cercando di catturare molte cose diverse e ci sono alcuni gruppi che non vuoi catturare.

Questo è praticamente il motivo per cui esistono. Mentre impari sui gruppi, scopri i gruppi atomici , fanno molto! Esistono anche gruppi di ricerca, ma sono un po 'più complessi e non utilizzati molto.

Esempio di utilizzo in seguito nella regex (backreference):

<([A-Z][A-Z0-9]*)\b[^>]*>.*?</\1> [Trova un tag xml (senza supporto ns)]

([A-Z][A-Z0-9]*) è un gruppo di acquisizione (in questo caso è il tagname)

Più avanti nel regex è \1che significa che corrisponderà solo allo stesso testo che era nel primo gruppo (il ([A-Z][A-Z0-9]*)gruppo) (in questo caso corrisponde al tag di fine).


potresti dare un semplice esempio di come verrà utilizzato in seguito per abbinare OR?
never_had_a_name,

intendo che puoi usare per abbinare più tardi o puoi usarlo nella sostituzione. L'o in quella frase era solo per mostrarti che ci sono due usi per un gruppo di cattura
Bob Fincheimer,

9

Beh, sono uno sviluppatore JavaScript e cercherò di spiegarne il significato relativo a JavaScript.

Prendi in considerazione uno scenario in cui desideri abbinare cat is animal quando desideri abbinare il gatto e l'animale ed entrambi dovrebbero avere una via isdi mezzo.

 // 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 espressioni regolari complesse è possibile che si verifichi la situazione in cui si desidera utilizzare un gran numero di gruppi, alcuni dei quali sono presenti per la corrispondenza ripetitiva e altri per fornire riferimenti a ritroso. Per impostazione predefinita, il testo corrispondente a ciascun gruppo viene caricato nell'array di backreference. Laddove abbiamo molti gruppi e dobbiamo solo essere in grado di fare riferimento ad alcuni di essi dall'array di backreference, possiamo ignorare questo comportamento predefinito per dire all'espressione regolare che alcuni gruppi sono lì solo per la gestione delle ripetizioni e non devono essere catturati e archiviati nella matrice di backreference.


7

Non posso commentare le risposte migliori per dire questo: vorrei aggiungere un punto esplicito che è implicito solo nelle risposte migliori:

Il gruppo (?...) non acquisente non rimuove alcun carattere dalla corrispondenza completa originale, ma riorganizza visivamente la regex al programmatore.

Per accedere a una parte specifica della regex senza caratteri estranei definiti è sempre necessario utilizzare .group(<index>)


2
Hai fornito il suggerimento più importante che mancava nel resto delle risposte. Ho provato tutti gli esempi in essi e usando il più eloquente delle imprecazioni, in quanto non ho ottenuto il risultato desiderato. Solo il tuo post mi ha mostrato dove ho sbagliato.
Seshadri R,

Felice di sentirlo!
Scott Anderson,

6

tl; dr gruppi non catturanti, come suggerisce il nome sono le parti della regex che non si desidera includere nella partita ed ?:è un modo per definire un gruppo come non catturante.

Supponiamo che tu abbia un indirizzo email example@example.com. La seguente regex creerà due gruppi , la parte id e la parte @ example.com. (\p{Alpha}*[a-z])(@example.com). Per semplicità, stiamo estraendo l'intero nome di dominio incluso il @carattere.

Ora diciamo, hai solo bisogno della parte id dell'indirizzo. Quello che vuoi fare è afferrare il primo gruppo del risultato della partita, circondato dalla ()regex e il modo per farlo è usare la sintassi del gruppo non catturante, ad es ?:. Quindi regex (\p{Alpha}*[a-z])(?:@example.com)restituirà solo la parte id dell'e-mail.


5

Una cosa interessante che mi sono imbattuto è il fatto che puoi avere un gruppo di cattura all'interno di un gruppo di non cattura. Dai un'occhiata a regex di seguito per gli URL web corrispondenti:

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

Immettere la stringa dell'URL:

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

Il primo gruppo nel mio regex (?:([A-Za-z]+):)è un gruppo non acquisente che corrisponde allo schema del protocollo e al :carattere dei due punti , ad esempio, http:ma quando stavo correndo sotto il codice, stavo vedendo che il primo indice dell'array restituito conteneva la stringa httpquando pensavo che httpe due punti :entrambi non verranno segnalati in quanto si trovano all'interno di un gruppo non di acquisizione.

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

inserisci qui la descrizione dell'immagine

Ho pensato che se il primo gruppo (?:([A-Za-z]+):)è un gruppo non acquisibile, allora perché restituisce una httpstringa nell'array di output.

Quindi, se noti che c'è un gruppo nidificato ([A-Za-z]+)all'interno del gruppo non di acquisizione. Quel gruppo nidificato ([A-Za-z]+)è un gruppo di acquisizione (che non ha ?:all'inizio) in se stesso all'interno di un gruppo di acquisizione (?:([A-Za-z]+):). Ecco perché il testo httpviene ancora acquisito ma il :carattere dei due punti che si trova all'interno del gruppo non acquisito ma al di fuori del gruppo acquisito non viene riportato nell'array di output.


2

Apri il tuo devTools di Google Chrome e quindi la scheda Console: e digita questo:

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

Eseguilo e vedrai:

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

Il JavaScriptmotore RegExp acquisisce tre gruppi, gli elementi con indici 1,2,3. Ora usa il segno non cattura per vedere il risultato.

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

Il risultato è:

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

Questo è ovvio cos'è un gruppo che non sta catturando.


2

Penso che ti darei la risposta. Non utilizzare le variabili di acquisizione senza verificare che la corrispondenza abbia avuto esito positivo.

Le variabili di acquisizione $1, ecc., Non sono valide a meno che la corrispondenza non sia riuscita e non vengono cancellate.

#!/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";
}

Nell'esempio sopra $1, (?:)viene utilizzato per evitare di catturare Bronto .

Se il modello viene abbinato, $1viene acquisito come modello raggruppato successivo.

Quindi, l'output sarà il seguente:

Fred wants a burger

È utile se non desideri che le partite vengano salvate.


1

È estremamente semplice, possiamo capire con un semplice esempio di data, supponiamo che la data sia menzionata come 1 gennaio 2019 o 2 maggio 2019 o qualsiasi altra data e vogliamo semplicemente convertirla nel formato gg / mm / aaaa non avremmo bisogno del mese nome che è gennaio o febbraio per quella materia, quindi per catturare la parte numerica, ma non il suffisso (facoltativo) puoi usare un gruppo non di acquisizione.

quindi l'espressione regolare sarebbe

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

E 'così semplice.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.