JavaRush /Java Blog /Random-IT /RegEx: 20 brevi passaggi per padroneggiare le espressioni...
Artur
Livello 40
Tallinn

RegEx: 20 brevi passaggi per padroneggiare le espressioni regolari. Parte 4

Pubblicato nel gruppo Random-IT
RegEx: 20 brevi passaggi per padroneggiare le espressioni regolari. Parte 1 RegEx: 20 brevi passaggi per padroneggiare le espressioni regolari. Parte 2 20 brevi passi per padroneggiare le espressioni regolari. Parte 3 Questa parte finale, a metà, toccherà gli argomenti utilizzati principalmente dai maestri delle espressioni regolari. Ma il materiale delle parti precedenti è stato facile per te, giusto? Ciò significa che puoi maneggiare questo materiale con la stessa facilità! Originale qui RegEx: 20 brevi passaggi per padroneggiare le espressioni regolari.  Parte 4 - 1 <h2>Passaggio 16: gruppi senza acquisire (?:)</h2> RegEx: 20 brevi passaggi per padroneggiare le espressioni regolari.  Parte 4 - 2Nei due esempi del passaggio precedente, stavamo catturando del testo di cui non avevamo realmente bisogno. Nell'attività Dimensioni file abbiamo acquisito gli spazi prima della prima cifra delle dimensioni del file e nell'attività CSV abbiamo acquisito le virgole tra ciascun token. Non abbiamo bisogno di catturare questi caratteri, ma dobbiamo usarli per strutturare la nostra espressione regolare. Queste sono le opzioni ideali per utilizzare un gruppo senza acquisire file (?:). Un gruppo che non cattura fa esattamente quello che sembra: consente di raggruppare e utilizzare i caratteri nelle espressioni regolari, ma non li cattura in un gruppo numerato:
pattern: (?:")([^"]+)(?:") 
string: Voglio solo "il testo tra queste virgolette" .
corrispondenze:             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 
gruppo:                 111111111111111111111111111    
( Esempio ) L'espressione regolare ora corrisponde al testo citato così come ai caratteri delle virgolette stessi, ma il gruppo di acquisizione ha acquisito solo il testo citato. Perché dovremmo farlo? Il punto è che la maggior parte dei motori di espressioni regolari ti consente di recuperare il testo dai gruppi di acquisizione definiti nelle tue espressioni regolari. Se possiamo tagliare i caratteri extra che non ci servono senza includerli nei nostri gruppi di acquisizione, sarà più semplice analizzare e manipolare il testo in seguito. Ecco come pulire il parser CSV dal passaggio precedente:
modello: (?:^|,)\s*(?:\"([^",]*)\"|([^", ]*)) 
stringa:   a , " b ", " cd ", e , f , " gh ", dfgi ,, k , "", l 
corrisponde a: ^ ^ ^^^ ^ ^ ^^^ ^^^^ ^ ^ 
gruppo:    2 1 111 2 2 111 2222 2 2    
( Esempio ) Ci sono alcune cose da <mark>notare qui:</mark> Innanzitutto, non cattureremo più le virgole poiché abbiamo cambiato il gruppo di acquisizione (^|,)in un gruppo non di acquisizione (?:^|,). In secondo luogo, abbiamo nidificato il gruppo di acquisizione all'interno del gruppo non di acquisizione. Ciò è utile quando, ad esempio, hai bisogno che un gruppo di caratteri appaia in un ordine specifico, ma ti interessa solo un sottoinsieme di quei caratteri. Nel nostro caso, avevamo bisogno che i caratteri senza virgolette e le virgolette [^",]*apparissero tra virgolette, ma in realtà non avevamo bisogno dei caratteri delle virgolette stesse, quindi non era necessario catturarli. Infine, <mark>nota</mark> che nell'esempio sopra c'è anche una corrispondenza di lunghezza zero tra i caratteri ke l. Le virgolette ""rappresentano la sottostringa cercata, ma non ci sono caratteri tra le virgolette, quindi la sottostringa corrispondente non contiene caratteri (lunghezza zero). <h3>Consolidiamo le nostre conoscenze? Ecco due attività e mezzo che ci aiuteranno in questo:</h3> Utilizzando gruppi non di acquisizione (e gruppi di acquisizione, classi di caratteri, ecc.), scrivere un'espressione regolare che catturi solo le dimensioni di file correttamente formattati sulla riga sotto :
modello:
stringa:   6,6 KB 1,3 KB 12 KB 5G 3,3 MB KB ,6,2 TB 9 MB .
corrispondenze: ^^^^^ ^^^^^ ^^^^^^ ^^^^ 
gruppo:    11111 1111 11111 111    
( Soluzione ) I tag di apertura HTML iniziano <e finiscono con >. I tag di chiusura HTML iniziano con una sequenza di caratteri </e terminano con il carattere >. Il nome del tag è contenuto tra questi caratteri. Puoi scrivere un'espressione regolare per acquisire solo i nomi nei seguenti tag? (Potresti essere in grado di risolvere questo problema senza utilizzare gruppi non di acquisizione. Prova a risolverlo in due modi! Una volta con i gruppi e una volta senza.)
modello:
stringa:   <p> </span> <div> </kbd> <link> 
corrisponde a: ^^^ ^^^^^^ ^^^^^ ^^^^^^ ^^^^^^ 
gruppo:    1 1111 111 111 1111    
( Soluzione che utilizza gruppi non di acquisizione ) ( Soluzione senza utilizzare gruppi non di acquisizione ) <h2>Passaggio 17: backlink \Ne gruppi di acquisizione con nome</h2> RegEx: 20 brevi passaggi per padroneggiare le espressioni regolari.  Parte 4 - 3Anche se ti ho avvertito nell'introduzione che provare a creare un parser HTML utilizzando le espressioni regolari di solito porta all'angoscia, quest'ultimo esempio è un bel passaggio a un'altra (a volte) utile caratteristica della maggior parte delle espressioni regolari: i riferimenti all'indietro. I backlink sono come gruppi ripetitivi in ​​cui puoi provare a catturare lo stesso testo due volte. Ma differiscono in un aspetto importante: cattureranno solo lo stesso testo, carattere per carattere. Mentre un gruppo ripetitivo ci permetterà di catturare qualcosa di simile a questo:
modello: (he(?:[az])+) 
stringa:   heyabcdefg hey heyo hegiallo heyyyyyyyyy 
corrisponde a: ^^^^^^^^^^ ^^^ ^^^^ ^^^^^^^^ ^^^ ^^^^^^^^ 
gruppo:    1111111111 111 1111 11111111 11111111111    
( Esempio ) ...allora il backlink corrisponderà solo a questo:
modello: (he([az])(\2+)) 
stringa: heyabcdefg hey heyo hegiallo heyyyyyyyyy 
corrisponde a:                              ^^^^^^^^^^^ 
gruppo:                                 11233333333    
( Esempio ) I gruppi di acquisizione ripetuti sono utili quando si desidera abbinare ripetutamente lo stesso modello, mentre i backlink sono utili quando si desidera abbinare lo stesso testo. Ad esempio, potremmo utilizzare un backlink per provare a trovare tag HTML di apertura e chiusura corrispondenti:
modello: <(\w+)[^>]*>[^<]+<\/\1> 
stringa:   <span style="color: red">ehi</span> 
corrisponde a: ^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 
gruppo:    1111    
( Esempio ) <mark>Nota</mark> che questo è un esempio estremamente semplificato e consiglio vivamente di non provare a scrivere un parser HTML basato su espressioni regolari. Questa è una sintassi molto complessa e molto probabilmente ti farà star male. I gruppi di acquisizione con nome sono molto simili ai backlink, quindi li tratterò brevemente qui. L'unica differenza tra i backreference e un gruppo Capture con nome è che... un gruppo Capture con nome ha un nome:
pattern: <(?<tag>\w+)[^>]*>[^<]+<\/(?P=tag)></tag> 
stringa:   <span style="color: red">hey< /span> 
corrisponde a: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 
gruppo:    1111    
( Esempio ) È possibile creare un gruppo di acquisizione denominato utilizzando la sintassi (?<nome>...) o (?'nome'...) (espressione regolare compatibile con .NET) o con questa sintassi (?P<nome>. ..) o (?P'name'...) (espressione regolare compatibile con Python). Poiché stiamo utilizzando PCRE (Perl compatibile Regular Expression) che supporta entrambe le versioni, qui possiamo utilizzarne una o l'altra. (Java 7 ha copiato la sintassi .NET, ma solo la versione con parentesi angolari. Nota del traduttore) Per ripetere un gruppo di acquisizione con nome successivamente in un'espressione regolare, utilizziamo \<kname> o \k'name' (.NET) o (? P= nome) (Python). Ancora una volta, PCRE supporta tutte queste diverse opzioni. Puoi leggere ulteriori informazioni sui gruppi di acquisizione con nome qui , ma questo è la maggior parte di ciò che devi veramente sapere su di essi. <h3>Compito per aiutarci:</h3> Usa i backlink per aiutarmi a ricordare... ummm... il nome di questa persona.
modello:
stringa: "Ciao, mi chiamo Joe." [più tardi] "Come si chiama quel ragazzo? Joe ?"
corrispondenze:        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^ 
gruppo:                  111    
( Soluzione ) <h2>Passaggio 18: lookahead e lookbehind</h2> RegEx: 20 brevi passaggi per padroneggiare le espressioni regolari.  Parte 4 - 4Ora approfondiremo alcune delle funzionalità avanzate delle espressioni regolari. Uso tutto fino al passaggio 16 abbastanza spesso. Ma questi ultimi passaggi sono solo per le persone che usano le espressioni regolari molto seriamente per trovare espressioni molto complesse. In altre parole, maestri delle espressioni regolari. "Guardare avanti" e "Guardare indietro" possono sembrare piuttosto complicati, ma in realtà non sono così complicati. Ti consentono di fare qualcosa di simile a quello che abbiamo fatto in precedenza con i gruppi non di acquisizione: controlla se c'è del testo immediatamente prima o immediatamente dopo il testo effettivo che vogliamo far corrispondere. Ad esempio, supponiamo di voler abbinare solo i nomi delle cose che piacciono alle persone, ma solo se ne sono entusiaste (solo se terminano la frase con un punto esclamativo). Potremmo fare qualcosa del tipo:
pattern: (\w+)(?=!) 
string: Mi piace la scrivania. Apprezzo la cucitrice. Amo la lampada !
partite:                                           ^^^^ 
girone:                                              1111    
( Esempio ) Puoi vedere come il gruppo di cattura sopra (\w+), che di solito corrisponde a qualsiasi parola nel passaggio, corrisponde solo alla parola lampada. Look-ahead positivo (?=!)significa che possiamo abbinare solo sequenze che terminano con !, ma in realtà non corrispondiamo al carattere del punto esclamativo stesso. Questa è una distinzione importante perché con i gruppi che non catturano stiamo abbinando il personaggio ma non lo catturiamo. Con lookaheads e lookbehinds, usiamo un carattere per costruire la nostra espressione regolare, ma poi non lo confrontiamo nemmeno con se stesso. Possiamo abbinarlo più tardi nella nostra espressione regolare. Esistono quattro tipi di lookahead e lookbehind: lookahead positivo (?=...), lookahead negativo (?!...), lookahead positivo (?<=...) e lookahead negativo (?<!. ..) . Fanno quello che sembrano: lookahead e lookbehind positivi consentono al motore delle espressioni regolari di continuare la corrispondenza solo quando il testo contenuto in lookahead/lookbehind corrisponde effettivamente. Lookahead e lookbehind negativi fanno l'opposto: consentono alla regex di corrispondere solo quando il testo contenuto nel lookahead/lookbehind non corrisponde. Ad esempio, vogliamo far corrispondere i nomi dei metodi solo in una catena di sequenze di metodi, non con l'oggetto su cui operano. In questo caso, ogni nome di metodo deve essere preceduto da un .. Un'espressione regolare che utilizza un semplice look-back può aiutare qui:
modello: (?<=\.)(\w+) 
stringa: myArray. flatMap.aggregate.summarise.print !
corrispondenze:         ^^^^^^^ ^^^^^^^^^ ^^^^^^^^^ ^^^^^ 
gruppo:            1111111 111111111 111111111 11111    
( Esempio ) Nel testo sopra, abbiniamo qualsiasi sequenza di caratteri di parole \w+, ma solo se sono preceduti dal carattere .. Potremmo ottenere qualcosa di simile utilizzando gruppi che non catturano, ma il risultato è un po' più confuso:
modello: (?:\.)(\w+) 
stringa: myArray .flatMap.aggregate.summarise.print !
corrispondenze:        ^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^ ^^^^^ 
gruppo:            1111111 111111111 111111111 11111    
( Esempio ) Anche se è più breve, corrisponde ai caratteri che non ci servono. Sebbene questo esempio possa sembrare banale, lookahead e lookbehind possono davvero aiutarci a ripulire le nostre espressioni regolari. <h3>Mancano pochissimi al traguardo! Le seguenti 2 attività ci porteranno un passo avanti più vicino ad esso:</h3> Lookbehind negativo (?<!...) consente al motore delle espressioni regolari di continuare a provare a trovare una corrispondenza solo se il testo contenuto all'interno del lookbehind negativo non lo è visualizzato fino al resto del testo, con il quale è necessario trovare una corrispondenza. Ad esempio, potremmo utilizzare un'espressione regolare per trovare la corrispondenza solo con i cognomi delle donne che partecipano a una conferenza. Per fare questo, vorremmo assicurarci che il cognome della persona non sia preceduto da un Mr.. Puoi scrivere un'espressione regolare per questo? (Si può presumere che i cognomi siano lunghi almeno quattro caratteri.)
modello:
stringa: Sig. Marrone, signorina. Smith , signora. Jones , la signorina Daisy , il sig. Verde
corrispondenze:                ^^^^^ ^^^^^ ^^^^^ 
gruppo:                   11111 11111 11111    
( Soluzione ) Diciamo che stiamo cancellando un database e abbiamo una colonna di informazioni che rappresenta le percentuali. Sfortunatamente, alcune persone hanno scritto i numeri come valori decimali nell'intervallo [0.0, 1.0], mentre altri hanno scritto percentuali nell'intervallo [0.0%, 100.0%] e altri ancora hanno scritto valori percentuali ma hanno dimenticato il segno percentuale letterale %. Usando il lookahead negativo (?!...), puoi contrassegnare solo quei valori che dovrebbero essere percentuali ma mancano cifre %? Questi devono essere valori rigorosamente superiori a 1.00, ma senza finale %. (Nessun numero può contenere più di due cifre prima o dopo il punto decimale.) <mark>Nota</mark> che questa soluzione è estremamente difficile . Se riesci a risolvere questo problema senza guardare la mia risposta, allora hai già enormi capacità nelle espressioni regolari!
modello:
stringa: 0,32 100,00 5,6 0,27 98% 12,2% 1,01 0,99% 0,99 13,13 1,10 
corrispondenze:      ^^^^^^ ^^^ ^^^^ ^^^^^ ^^^^ 
gruppo:         111111 111 1111 11111 1111    
( Soluzione ) <h2>Passaggio 19: Condizioni nelle espressioni regolari</h2> RegEx: 20 brevi passaggi per padroneggiare le espressioni regolari.  Parte 4 - 5Abbiamo ora raggiunto il punto in cui la maggior parte delle persone non utilizzerà più le espressioni regolari. Abbiamo coperto probabilmente il 95% dei casi d'uso per semplici espressioni regolari e tutto ciò che viene fatto nei passaggi 19 e 20 viene generalmente eseguito da un linguaggio di manipolazione del testo più completo come awk o sed (o un linguaggio di programmazione generico). Detto questo, andiamo avanti, giusto per sapere cosa può realmente fare un'espressione regolare. Sebbene le espressioni regolari non siano Turing complete , alcuni motori di espressioni regolari offrono funzionalità molto simili a un linguaggio di programmazione completo. Una di queste caratteristiche è la "condizione". I condizionali regex consentono istruzioni if-then-else, in cui il ramo scelto è determinato dal "guardare avanti" o dal "guardare indietro" di cui abbiamo appreso nel passaggio precedente. Ad esempio, potresti voler far corrispondere solo le voci valide in un elenco di date:
modello: (?<=Febbraio )([1-2][0-9])|(?<=Marzo )([1-2][0-9]|3[0-1]) 
stringa: Date lavorate : 28 febbraio , 29 febbraio , 30 febbraio, 30 marzo , 31 marzo  
partite:                   ^^ ^^ ^^ ^^ 
gruppo:                      11 11 22 22    
( Esempio ) <mark>Nota</mark> che i gruppi sopra indicati sono anche indicizzati per mese. Potremmo scrivere un'espressione regolare per tutti i 12 mesi e acquisire solo le date valide, che verrebbero poi combinate in gruppi indicizzati per mese dell'anno. Quanto sopra utilizza una sorta di struttura if-like che cercherà le corrispondenze nel primo gruppo solo se "Feb" precede un numero (e allo stesso modo per il secondo). Ma cosa succederebbe se volessimo utilizzare un'elaborazione speciale solo per febbraio? Qualcosa del tipo "se il numero è preceduto da "Feb", fai questo, altrimenti fai quest'altra cosa". Ecco come funzionano i condizionali:
modello: (?(?<=Feb )([1-2][0-9])|([1-2][0-9]|3[0-1])) 
stringa: Date lavorate: 28 feb , 29 febbraio , 30 febbraio , 30 marzo , 31 marzo  
partite:                   ^^ ^^ ^^ ^^ 
gruppo:                      11 11 22 22    
( Esempio ) La struttura if-then-else assomiglia a (?(If)then|else), dove (if) è sostituito da "guarda avanti" o "guarda indietro". Nell'esempio sopra, (if) è scritto come (?<=Feb). Puoi vedere che abbiamo abbinato date superiori a 29, ma solo se non seguono "febbraio". L'uso dei lookbehind nelle espressioni condizionali è utile se vuoi assicurarti che la corrispondenza sia preceduta da del testo. I condizionali lookahead positivi possono creare confusione perché la condizione stessa non corrisponde ad alcun testo. Quindi, se vuoi che la condizione if abbia mai un valore, deve essere paragonabile a lookahead come di seguito:
modello: (?(?=esatto)esatto|else)wo 
stringa: esatto else esatto due altre  
corrispondenze:            ^^^^^^^ ^^^^^^
( Esempio ) Ciò significa che i condizionali lookahead positivi sono inutili. Controlli per vedere se quel testo è in primo piano e quindi fornisci uno schema corrispondente da seguire quando lo è. L'espressione condizionale non ci aiuta affatto qui. Puoi anche semplicemente sostituire quanto sopra con un'espressione regolare più semplice:
modello: (?:esatto|else)wo 
stringa: esatto else esatto due altre  
corrispondenze:            ^^^^^^^ ^^^^^^
( Esempio ) Quindi, la regola pratica per le espressioni condizionali è: prova, prova e prova ancora. Altrimenti, le soluzioni che ritieni ovvie falliranno nei modi più entusiasmanti e inaspettati :) <h3>Eccoci arrivati ​​all'ultimo blocco di attività che ci separa dall'ultimo, il 20° passaggio:</h3> Scrivi un'espressione regolare che utilizza l'espressione condizionale lookahead negativa per verificare se la parola successiva inizia con una lettera maiuscola. Se è così, prendi solo una lettera maiuscola e poi le lettere minuscole. In caso contrario, prendi qualsiasi carattere di parola.
modello:
stringa:   Jones Smith 9sfjn Hobbes 23r4tgr9h CSV Csv vVv 
corrispondenze: ^^^^^ ^^^^^ ^^^^^ ^^^^^^ ^^^^^^^^^ ^^^ ^^^ 
gruppo:    22222 22222 11111 222222 111111111 222 111    
( Soluzione ) Scrivere un'espressione condizionale lookbehind negativa che catturi il testo ownssolo se non è preceduto da testo cle che catturi il testo oudssolo quando è preceduto da testo cl. (Un esempio un po' artificioso, ma cosa si può fare...)
modello:
string: Quei pagliacci possiedono delle nuvole . oud.
corrispondenze:              ^^^^ ^^^^   
( Soluzione ) <h2>Passaggio 20: Ricorsione e ulteriore studio</h2> RegEx: 20 brevi passaggi per padroneggiare le espressioni regolari.  Parte 4 - 6In effetti, c'è molto che può essere racchiuso in un'introduzione in 20 passaggi a qualsiasi argomento e le espressioni regolari non fanno eccezione. Esistono molte implementazioni e standard diversi per le espressioni regolari che possono essere trovati su Internet. Se vuoi saperne di più, ti suggerisco di dare un'occhiata al meraviglioso sito regolariexpressions.info , è un riferimento fantastico e sicuramente da lì ho imparato molto sulle espressioni regolari. Lo consiglio vivamente, così come regex101.com per testare e pubblicare le tue creazioni. In questo passaggio finale ti darò qualche conoscenza in più sulle espressioni regolari, ovvero su come scrivere espressioni ricorsive. Le ricorsioni semplici sono piuttosto semplici, ma pensiamo a cosa significa nel contesto di un'espressione regolare. La sintassi per la ricorsione semplice in un'espressione regolare è scritta in questo modo: (?R)?. Ma, ovviamente, questa sintassi deve apparire all'interno dell'espressione stessa. Ciò che faremo è annidare l'espressione al suo interno, un numero arbitrario di volte. Per esempio:
modello: (hey(?R)?oh) 
stringa:   heyoh heyyoh heyheyohoh hey oh heyhey ehi heyheyohoh  
corrisponde a: ^^^^^ ^^^^^^^^^^ ^^^^^^^^^^ 
gruppo:    11111 1111111111 1111111111    
( Esempio ) Poiché l'espressione nidificata è facoltativa ( (?R)follow ?), la corrispondenza più semplice è semplicemente ignorare completamente la ricorsione. Quindi, heye quindi ohcorrisponde a ( heyoh). Per trovare una corrispondenza con qualsiasi espressione più complessa di questa, dobbiamo trovare la sottostringa corrispondente annidata al suo interno nel punto dell'espressione in cui abbiamo inserito (?R)la sequenza. In altre parole, potremmo trovare heyheyohoh o heyheyheyohohoh e così via. Uno degli aspetti migliori di queste espressioni annidate è che, a differenza dei riferimenti all'indietro e dei gruppi di acquisizione con nome, non ti limitano al testo esatto che hai abbinato in precedenza, carattere per carattere. Per esempio:
modello: ([Hh][Ee][Yy](?R)?oh) 
stringa:   heyoh heyyoh hEyHeYohoh hey oh heyhey hEyHeYHEyohohoh  
corrisponde a: ^^^^^ ^^^^^^^^^^ ^^^^^ ^^^^^^^^^^ 
gruppo:    11111 1111111111 111111111111111    
( Esempio ) Puoi immaginare che il motore delle espressioni regolari copi e incolli letteralmente la tua espressione regolare in se stessa un numero arbitrario di volte. Naturalmente, ciò significa che a volte potrebbe non fare ciò che avresti sperato:
modello: ((?:\(\*)[^*)]*(?R)?(?:\*\))) 
stringa: (* commento (* annidato *) non *)
partite:            ^^^^^^^^^^^^^ 
gruppo:               111111111111    
( Esempio ) Puoi spiegare perché questa regex ha catturato solo il commento nidificato e non il commento esterno? Una cosa è certa: quando scrivi espressioni regolari complesse, testale sempre per assicurarti che funzionino come pensi. Questo rally ad alta velocità lungo le strade delle espressioni regolari è giunto al termine. Spero che questo viaggio ti sia piaciuto. Bene, ed infine lascio qui, come promesso all'inizio, diversi link utili per uno studio più approfondito della materia:
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION