Che cosa è un gruppo non-cattura nelle espressioni regolari?
-
29-09-2019 - |
Domanda
Come vengono gruppi non-cattura, vale a dire (?:)
, usati nelle espressioni regolari e che cosa sono buone per?
Soluzione
Vorrei cercare di spiegare con un esempio.
Si consideri il seguente testo:
http://stackoverflow.com/
https://stackoverflow.com/questions/tagged/regex
Ora, se applico la regex di sotto sopra ...
(https?|ftp)://([^/\r\n]+)(/[^\r\n]*)?
... Mi sarebbe ottenere il seguente risultato:
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"
Ma non mi interessa circa il protocollo - voglio solo l'host e il percorso dell'URL. Quindi, io cambio la regex per includere il non-cattura (?:)
gruppo.
(?:https?|ftp)://([^/\r\n]+)(/[^\r\n]*)?
Ora, il mio aspetto risultato come questo:
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"
Vedi? Il primo gruppo non è stato catturato. Gli usi parser modo che corrisponda al testo, ma lo ignora più tardi, nel risultato finale.
EDIT:
Come richiesto, mi permetta di provare a spiegare i gruppi troppo.
Bene, gruppi servono vari scopi. Possono aiutare a estrarre informazioni esatte da una partita più grande (che può anche essere chiamato), essi consentono di rivincita di un gruppo abbinato precedente, e possono essere utilizzati per sostituzioni. Proviamo alcuni esempi, che ne dite?
Ok, immaginate di avere un qualche tipo di XML o HTML (essere consapevoli che espressione regolare non può essere lo strumento migliore per il lavoro , ma è bello come un esempio). Si vuole analizzare i tag, così si potrebbe fare qualcosa di simile (ho aggiunto gli spazi per rendere più facile da capire):
\<(?<TAG>.+?)\> [^<]*? \</\k<TAG>\>
or
\<(.+?)\> [^<]*? \</\1\>
La prima regex ha un gruppo di nome (TAG), mentre il secondo utilizza un gruppo comune. Entrambe le espressioni regolari fanno la stessa cosa: usano il valore dal primo gruppo (il nome del tag) per abbinare il tag di chiusura. La differenza è che il primo utilizza il nome corrisponda al valore, e il secondo utilizza l'indice del gruppo (che inizia a 1).
Proviamo alcune sostituzioni ora. Si consideri il seguente testo:
Lorem ipsum dolor sit amet consectetuer feugiat fames malesuada pretium egestas.
Ora, usiamo questa espressione regolare muto su di esso:
\b(\S)(\S)(\S)(\S*)\b
Questo corrisponde all'espressione regolare le parole con almeno 3 caratteri e gruppi di usi 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, poi il secondo gruppo, aggiungere un altro di sottolineatura, e poi 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.
È possibile utilizzare i gruppi chiamato per le sostituzioni troppo, utilizzando ${name}
.
Per giocare con regex, vi consiglio http://regex101.com/ , che offre una buona quantità di dettagli su come funziona l'espressione regolare; offre anche un paio di motori regex da scegliere.
Altri suggerimenti
È possibile utilizzare gruppi di cattura per organizzare e analizzare un'espressione. Un gruppo non-cattura ha il primo vantaggio, ma non ha l'overhead del secondo. Si può ancora dire un gruppo non-cattura è facoltativo, per esempio.
Dire che si desidera abbinare il testo numerico, ma alcuni numeri potrebbe essere scritto come 1 °, 2 °, 3 °, 4 °, ... Se si desidera catturare la parte numerica, ma non il suffisso (opzionale) è possibile utilizzare un non gruppo -capturing.
([0-9]+)(?:st|nd|rd|th)?
che abbinerà i numeri nella forma 1, 2, 3 ... o sotto forma 1 °, 2 °, 3 °, ... ma sarà catturare solo la parte numerica.
?:
viene utilizzato quando si desidera raggruppare un'espressione, ma non si vuole salvarlo come / porzione catturata abbinato della stringa.
Un esempio potrebbe essere qualcosa per abbinare un indirizzo IP:
/(?:\d{1,3}\.){3}\d{1,3}/
Si noti che non mi interessa di salvare i primi 3 ottetti, ma il raggruppamento (?:...)
mi permette di accorciare la regex senza incorrere l'overhead di catturare e immagazzinare un match.
Si rende il gruppo non-cattura, il che significa che la stringa corrispondente al gruppo che non sarà incluso nella lista delle catture. Un esempio in Ruby per illustrare la differenza:
"abc".match(/(.)(.)./).captures #=> ["a","b"]
"abc".match(/(?:.)(.)./).captures #=> ["b"]
motivazione storica: L'esistenza di gruppi non-cattura può essere spiegato con l'uso delle parentesi. Considerate le espressioni (a | b) c ed un | bc, a causa di priorità del concatenamento sopra |, queste espressioni rappresentano due lingue diverse ({ac, bc} e {a, bc} rispettivamente). Tuttavia, la parentesi sono utilizzati anche come un gruppo di corrispondenza (come spiegato dalle altre risposte ...).
Quando si desidera avere parentesi ma non catturare l'sottoespressione si utilizza gruppi non-cattura. Nell'esempio, (:? Un | b) c
I gruppi che cattura è possibile utilizzare più tardi nel regex per abbinare o è possibile utilizzarli nella parte sostituzione del regex. Fare un non cattura gruppo esenta semplicemente che gruppo sia utilizzato per uno di questi motivi.
gruppi non-cattura sono grandi se si sta cercando di catturare molte cose diverse e ci sono alcuni gruppi che non si desidera catturare.
Questo è più o meno la ragione per cui esiste. Mentre si sta imparando sui gruppi, conoscere atomica Gruppi , fanno un sacco! Ci sono anche gruppi di Lookaround ma sono un po 'più complesso e non utilizzati tanto.
Esempio di utilizzo più avanti nel regolare (backreference):
<([A-Z][A-Z0-9]*)\b[^>]*>.*?</\1>
[Finds un tag XML (senza supporto ns)]
([A-Z][A-Z0-9]*)
è un gruppo di cattura (in questo caso è la TagName)
Più tardi nel regex è \1
che significa che corrisponderà solo lo stesso testo che era nel primo gruppo (il gruppo ([A-Z][A-Z0-9]*)
) (in questo caso si sta abbinando il tag finale).
Fammi 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)
-> Non Captured Gruppo 1
(?:=)
-> Non Captured Gruppo 2
(\w+)
-> Captured gruppo 1
(,)
-> Captured Gruppo 2
\1
-> risultato di gruppo catturato 1 cioè In linea 1 è il gatto, in linea 2 è il gatto, in linea 3 è cane
\2
-> risultato di gruppo catturato 2 cioè virgola (,)
Quindi, in questo codice, dando \ 1 e \ 2 ricordiamo o ripetere il risultato di gruppo catturato 1 e 2, rispettivamente, più avanti nel codice.
secondo l'ordine di codice (:? Animale) deve essere di gruppo 1 e (:? =) Dovrebbe essere del gruppo 2 e continua ..
, ma dando al: facciamo il non match-gruppo acquisito (che non contano fuori nel gruppo abbinato, quindi il numero di raggruppamento parte dal gruppo prima catturato e non il non catturati), in modo tale che la ripetizione del risultato di match-gruppo (:? animale). non può essere chiamato più avanti nel codice
Spero che questo spiega l'uso di gruppo non cattura.
Anche io sono uno sviluppatore JavaScript e cercherò di spiegare il suo significato di pertinenza JavaScript.
Si consideri uno scenario in cui si desidera far corrispondere cat is animal
quando si desidera partita gatto e degli animali ed entrambi dovrebbe avere un is
tra di loro.
// 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"]
Nelle espressioni regolari complesse si può avere la situazione sorgono in cui si desidera utilizzare un gran numero di gruppi, alcuni dei quali ci sono per la corrispondenza di ripetizione e alcuni dei quali sono lì per fornire riferimenti all'indietro. Per default il testo corrispondente ciascun gruppo viene caricato nella matrice backreference. Dove abbiamo un sacco di gruppi e solo bisogno di essere in grado di fare riferimento a alcuni di loro dalla matrice backreference possiamo ignorare questo comportamento predefinito per dire l'espressione regolare che alcuni gruppi sono lì solo per la manipolazione ripetizione e non hanno bisogno di essere catturato e immagazzinato nell'array backreference.
Non posso commentare le migliori risposte per dire questo: Vorrei aggiungere un punto esplicito, che è implicito solo nelle migliori risposte:
Il (?...)
gruppo non-cattura
fa Non rimuovere i caratteri dalla partita piena originale, solo riorganizza la regex visivamente al programmatore.
Per accedere a una parte specifica del regex senza caratteri estranei definiti si sarebbe sempre necessario utilizzare .group(<index>)
tl; dr non gruppi di cattura, come suggerisce il nome sono le parti del regex che non si desidera essere inclusi nella partita e ?:
è un modo per definire un gruppo come essere non cattura.
esempio di farvi avere un example@example.com
indirizzo di posta elettronica. La seguente espressione regolare creerà due Gruppi , la parte ID e @ example.com parte. (\p{Alpha}*[a-z])(@example.com)
. Per semplicità, noi estraiamo l'intero nome di dominio compreso il carattere @
.
Ora diciamo, è necessario solo la parte id dell'indirizzo. Che cosa si vuole fare è quello di afferrare il primo gruppo del risultato della partita, circondato da ()
nel regex e il modo per farlo è quello di utilizzare la sintassi non-gruppo di cattura, cioè ?:
. Così il (\p{Alpha}*[a-z])(?:@example.com)
regex restituirà solo la parte ID del email.
Una cosa interessante che ho trovato è il fatto che si può avere un gruppo di cattura all'interno di un gruppo non-cattura. Date un'occhiata al di sotto espressioni regolari per la corrispondenza URL web:
var parse_url_regex = /^(?:([A-Za-z]+):)(\/{0,3})([0-9.\-A-Za-z]+)(?::(\d+))?(?:\/([^?#]*))?(?:\?([^#]*))?(?:#(.*))?$/;
stringa URL in ingresso:
var url = "http://www.ora.com:80/goodparts?q#fragment";
Il primo gruppo nel mio (?:([A-Za-z]+):)
regex è un gruppo non-cattura che corrisponde al sistema di protocollo e del colon carattere :
cioè http:
ma quando ho girato al di sotto del codice, mi è stato vedere il primo indice della matrice restituita è stato che contiene la http
stringa quando stavo pensando che http
e del colon :
entrambi non andranno segnalato come sono all'interno di un gruppo non-cattura.
console.debug(parse_url_regex.exec(url));
ho pensato che se il primo (?:([A-Za-z]+):)
gruppo è un gruppo non-cattura, allora il motivo per cui sta tornando stringa http
nella matrice di uscita.
Quindi, se si nota che c'è un ([A-Za-z]+)
gruppo nidificato all'interno del gruppo non-cattura. Che ([A-Za-z]+)
gruppo nidificato è un gruppo di cattura (non avendo ?:
all'inizio) di per sé all'interno di un (?:([A-Za-z]+):)
non gruppo di cattura. Ecco perché il http
testo ancora, viene catturato, ma il carattere due punti :
che è all'interno del gruppo non-cattura, ma al di fuori del gruppo di cattura non viene riportato nella matrice di uscita.
penso che darei la risposta, Non utilizzare le variabili di acquisizione senza verificare che la partita è riuscita.
Le variabili di cattura, $ 1, ecc, non sono validi a meno che la partita è riuscito, e non sono cancellati, sia.
#!/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 di cui sopra, per evitare la cattura Bronto in $ 1, (? :) viene utilizzato. Se il modello è abbinato, allora $ 1 viene catturato dal prossimo modello raggruppati. Così, l'uscita sarà come di seguito:
Fred wants a burger
E 'utile se non si desidera che le partite siano salvati.
Aprire il Google Chrome DevTools e poi scheda Console: e digitare questo:
"Peace".match(/(\w)(\w)(\w)/)
Esegui e si vedrà:
["Pea", "P", "e", "a", index: 0, input: "Peace", groups: undefined]
La cattura dei motori JavaScript
RegExp tre gruppi, gli elementi con indici 1,2,3. Ora utilizzare il marchio non cattura per vedere il risultato.
"Peace".match(/(?:\w)(\w)(\w)/)
Il risultato è:
["Pea", "e", "a", index: 0, input: "Peace", groups: undefined]
Questo è evidente che cosa è il gruppo non cattura.
La sua estremamente semplice, possiamo capire con una semplice esempio la data, supponiamo che se la data è indicata come 1 Gennaio 2019 o 2 Maggio 2019 o qualsiasi altra data e noi semplicemente vuole convertirlo in gg / mm / aaaa formato che non avrebbe bisogno il nome del mese che è gennaio o febbraio per quella materia, così al fine di catturare la parte numerica, ma non il (opzionale) suffisso è possibile utilizzare un non-gruppo di cattura.
quindi l'espressione regolare sarebbe,
([0-9]+)(?:January|February)?
La sua così semplice come sembra.