Daniele Irsuti - Frontend developer

Non so usare le Regex

Il titolo dice già abbastanza, ma forse occorre rimarcarlo: non so usare le regular expressions e ci ho scritto un post.

Perché farlo? Perché svergognarmi in pubblica piazza? Perché è una cosa che non si può non sapere usare ed è una lacuna da colmare immediatamente.

Paraculo mode on

Finora non mi è mai capitato di utilizzarle estensivamente, mi sono sempre limitato a farne un utilizzo tutto sommato basilare e per le cose più complesse mi sono sempre affidato a snippet presi qua e là che avevano ottimi feedback (non giudicarmi male, so che è una cosa che non fa nessuno 🙄)

Paraculo mode off

In questo periodo ho avuto la brillante idea di fare dei web scraper per hobby (giusto perché spendevo poco tempo sul codice giustamente lo faccio anche nel tempo libero) ed è nata la necessità di utilizzarle per estrapolare le informazioni specialmente laddove era lampante che la stringa seguiva un certo pattern.

Prima di struggermi nel cercare di capire cosa facessero tutti quei caratteri ho cercato di studiare un minimo di teoria cercando di capire le singole responsabilità di ciascun segno.

Ancoraggi: ^ $

La frase di esempio è “Non so usare Regex”.

  • ^Non so - Trova la corrispondenza con la parola che inizia con Non so -> Link
  • Regex$ - Trova la corrispondenza con la parola che finisce con Regex
  • ^Non so usare Regex$ - Trova la corrispondenza esatta con le parole che iniziano e finiscono per Non so usare Regex
  • usare - Trova la corrispondenza se esiste questa parola all’interno della stringa di testo

Quantificatori: * + ? {}

  • Non* - Trova la corrispondenza con una stringa formata da No seguita da 0 o più lettere n - Link
  • Non+ - Trova la corrispondenza con una stringa formata da No seguita da 1 o più lettere n
  • Non? - Trova la corrispondenza con una stringa formata da No seguita da 0 o una lettera n
  • Non{2} - Trova la corrispondenza con una stringa formata da No seguita da 2 lettere n
  • Non{2,} - Trova la corrispondenza con una stringa formata da No seguita da 2 o più lettere n
  • Non{2,3} - Trova la corrispondenza con una stringa formata da No seguita da 2 o 3 lettere n
  • Non(no)* - Trova la corrispondenza con una stringa formata da Non seguita da una o più ripetizioni della sequenza no. - Link

    • Prova in console log!

      "Nonnononononono".replace(/Non(no)*/, '👴')
  • Non(no){2,5} Trova la corrispondenza con una stringa formata da Non seguita da 2 fino a 5 ripetizioni della sequenza no

Operatore OR | oppure []

  • N(o|n) oppure N[on] - Trova la corrispondenza con una stringa che ha N seguita da o oppure n - Link

Classi di caratteri \d \w \s .

  • \d - Trova la corrispondenza di un singolo carattere che è una cifra - Link
  • \w - Trova la corrispondenza di qualsiasi carattere (compreso _)
  • \s - Trova la corrispondenza con qualsiasi carattere di spazio, spazio a capo, tab
  • . - Trova la corrispondenza con qualsiasi carattere

Per ognuno di questi caratteri è possible creare la loro forma negativa:

  \d => \D
  \s => \S
  \w => \W

Flags

Finora ho mostrato come costruire una Regex, ma ho tralasciato un aspetto molto importante: i flags. Una regex viene costruita nella forma /abc/, dove i delimitatori / contengono la stringa da ricercare. Alla fine di ogni delimitatore è possibile specificare uno o più flag (possono essere combinati tra loro) che indicano alcune di queste istruzioni:

  • g (global) - significa che il pattern di ricerca non si limita solo alla prima corrispondenza ma ricomincia subito dopo la prima stringa trovata.
  • m (multi-line) - Quando è abilitato ^ o $ la corrispondenza non viene ricercata esclusivamente alla prima linea della stringa, bensì all’inizio o alla fine di tutte le linee di testo presenti nella stringa
  • i (insensitive) - Render l’intera espressione di ricerca case-insensitive.

Qui l’esempio che include tutti i flag descritti Link


Grouping e Capturing ()

  • a(bc) - Le parentesi creano un gruppo di cattura di valore bc
  • a(?:bc)* - usando ?: disabilita il gruppo di cattura, in sostanza diventa piena corrispondenza senza che nessun gruppo venga creato.
  • a(?bc) - viene assegnato un nome al gruppo.

L’ultimo operatore è utilissimo, non sapevo la sua esistenza prima di scendere nel dettaglio su come funzionano le regex. Quest’ultimo consente di assegnare un nome al gruppo e si presta particolarmente quando dobbiamo estrapolare più di una informazione presente da una stringa. Prendiamo in esame la seguente stringa: 1990-05-12: la mia data di nascita. Servendomi di una regex, voglio recuperare le informazioni circa: giorno, mese, anno.

Di conseguenza la mia regex avrà questo pattern di ricerca:

/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/

Link

In javascript, è possibile estrapolare queste informazioni utilizzando questa api:

const data = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/.exec('1990-05-12')

// data conterrà una proprietà groups strutturata così:
// "{
//     "day": "05",
//     "month": "12",
//     "year": "1990"
// }"

Espressioni tra parentesi quadre []

  • [abc] oppure ** [a-c]- Corrisponde se una stringa ha una delle lettere in parentesi quadre
  • [a-fA-F0-9] - Corrisponde se una stringa rappresenta una singola cifra esadecimale

Corrispondenza Greedy e Lazy

I quantificatori *,+,{} sono operatori greedy, quindi possono espandere la corrispondenza su tutto il testo. Prendendo come esempio Questo è <div>un div</div>. Se si settasse la regex a <.+> il testo corrispondente sarebbe <div>un div</div>: questo è un tipo di Greedy Match.

Se si volesse invece ottenere soltanto <div> e </div> bisognerebbe aggiungere un ? alla regex corrente, ossia: <.+?> (o meglio <[^<$>]+>, più strict rispetto all’altra). Questa è un tipo esempio di Lazy Match.


Confini \b \B

  • \babc\b - corrisponde solo se la parola ricercata in tutta la stringa si trova da sola, delimitata tra spazi o interruzioni di linea - Link
  • \Babc\B - L’inverso della precedente: quindi la parola deve trovarsi all’interno di un’altra parola - Link

Conclusioni

Alla fine della giornata sono riuscito ad imparare parecchie cose sulle regex, finalmente riesco a fare qualcosa di complesso (impossibile, prima di oggi) e soprattutto: so come cercare. Non mi sono dilungato nelle Back-references e Look-ahead and Look-behind, per questo rimando direttamente all’articolo da cui ho preso gran parte di ciò che ho scritto oggi.

Link utili