Le espressioni regolari
Guida alla sintassi con esempi interattivi
È da un po' di tempo che meditavo la creazione di una guida alle espressioni regolari: in giro per la rete ce ne sono a bizzeffe, ma poche complete e pochissime in italiano; inoltre scrivere una guida dà la possibilità di ampliare le proprie conoscenze! Infatti scrivendola ho imparato cose di cui ignoravo l'esistenza!
Bisogna dire che molti preferiscono copiare e incollare righe su righe d'incomprensibili caratteri piuttosto che imparare questa strana sintassi, poiché a prima vista questa appare criptica e incomprensibile e le espressioni sono solo lunghe serie di caratteri messi a casaccio senza un nesso logico!
Ma vedrete che non c'è nulla di difficile: basta un po' di elasticità mentale, un pizzico di memoria e soprattutto un buon manuale da consultare spesso 😀.
Cercherò di utilizzare una linearità nella descrizione, anche se questo mi risulterà difficile poiché è complicato spiegare un singolo elemento senza utilizzarne altri.
A che cosa servono le espressioni regolari?
Le espressioni regolari sono molto utili nell'ambito della ricerca e della sostituzione. Immaginiamo di avere un testo storico enorme e di dover convertire tutte le date in formato americano (yyyy/mm/dd) in quello classico italiano…
Sarebbe un lavoraccio doverlo fare tutto a mano! E probabilmente ci sfuggirebbero degli errori!
Ecco, con le espressioni regolari è possibile farlo in pochi caratteri! Con un risparmio di tempo, di errori e di… fegato!
La sintassi: uguale per tutti i programmi / linguaggi?
Solitamente sì, in JavaScript e in Perl la sintassi è simile, come anche in PHP e in altri linguaggi.
Anche Ide e programmi di testo come vim, notepad++, Komodo edit, Dreamweaver, PhpStorm, Android Studio, etc supportano il search & replace con le espressioni regolari.
Diciamo che la sintassi potrebbe cambiare, ma non di molto. E comunque se ne imparate una potrete comunque cavarvela con qualsiasi altra variante.
Gli esempi che utilizzerò qui di seguito utilizzeranno JavaScript, in modo da poterli testare al volo.
Ma passiamo alla descrizione dettagliata di ogni singolo elemento:
Come si scrive un'espressione regolare
La sintassi di una espressione regolare di per sé è semplice: è sempre racchiusa da due delimitatori (in JavaScript e in molti altri linguaggi è lo slash /, in altri è possibile anche definirlo) all'interno dei quali sono presenti le regole di ricerca e alla fine sono presenti i modificatori (come per esempio i per il case insensitive, g per ricercare tutte le occorrenze e non solo la prima, ecc.).
╔═══╗╔═══════════╗╔═══╗╔═══╗
║ / ║║ [a-f0-9]+ ║║ / ║║ m ║
╚═╤═╝╚═════╤═════╝╚═╤═╝╚═╤═╝┌──────────────┐
┌─────────┴────┐ │ │ └──┤ modificatore │
│ delimitatore │ │ │ └──────────────┘
│ iniziale │ │ │ ┌──────────────┐
└──────────────┘ │ └─┤ delimitatore │
┌─────┴───────┐ │ finale │
│ espressione │ └──────────────┘
│ regolare │
└─────────────┘
Ecco un esempio di espressione regolare per ricercare un URL in un testo. Non spaventatevi, alla fine della guida riuscirete a comprenderla 🙂
// Trova tutti i link in un testo
const testo = `questo è un link: http://www.example.com/test
un altro link: https://example2.com/con-url/lungo.html
vai su "https://www.chalda.it" e non te ne pentirai!`;
const regex = /\b(https?):\/\/([\w\.]+)(\/[^\s"']*)?/g;
const match = testo.match(regex);
console.log(match);
Ah, tutti gli esempi sono presentati in javaScript, e sono eseguibili cliccando sul pulsante ▶, ed è possibile modificare il testo, l'espressione regolare e i modificatori, quindi potete divertirvi e sbizzarrirvi come volete! 🙂
Le funzioni utilizzate sono match per la ricerca e replace per la sostituzione
I metacaratteri
Nelle espressioni regolari esistono diversi “caratteri speciali” dalle diverse funzioni:
.Il punto (qualsiasi carattere)- Il punto significa qualsiasi carattere a eccezione di quelli che identificano una riga nuova (
\ne\rper intenderci)
// Trova una s seguita da 3 caratteri
const testo = `Espressioni regolari!`;
const regex = /s.../g;
const match = testo.match(regex);
console.log(match);
^L'accento circonflesso (inizio riga)- L'accento circonflesso identifica l'inizio di una riga; inoltre all'inizio di un gruppo nega il gruppo stesso (vedi i gruppi)
// Trova i primi due caratteri
const testo = `Espressioni regolari!`;
const regex = /^../;
const match = testo.match(regex);
console.log(match);
$Il dollaro (fine riga)- Il dollaro identifica la fine di una riga
// Trova gli ultimi due caratteri
const testo = `Espressioni regolari!`;
const regex = /..$/;
const match = testo.match(regex);
console.log(match);
()Le parentesi tonde (gruppi)- Le parentesi tonde identificano i gruppi di caratteri
// Trova ss o sp
const testo = `Espressioni regolari!`;
const regex = /(ss|sp)/g;
const match = testo.match(regex);
console.log(match);
|Il pipe (or)- Il pipe è una condizione OR da inserire ne i gruppi
// Trova tutte le lettere r seguite da una vocale
const testo = `Espressioni regolari!`;
const regex = /r(a|i|u|o|e)/g;
const match = testo.match(regex);
console.log(match);
[]Le parentesi quadrate (intervalli e classi)- Le parentesi quadrate identificano le classi e gli intervalli di caratteri
\La barra (escape)- La barra (backslash) annulla gli effetti del metacarattere successivo
// Trova solo il punto e non qualsiasi carattere
const testo = ` Espressioni . regolari!`;
const regex = /\./g;
const match = testo.match(regex);
console.log(match);
I quantificatori
I quantificatori, come dice il termine stesso, indicano quante volte ricercare una data sequenza di caratteri. Si posizionano dopo l'elemento da ricercare
+Il più (>=1)- Indica 1 o più occorrenze
// Trova una o più s seguite da i
const testo = `Espressioni, pesi, piume`;
const regex = /s+i/g;
const match = testo.match(regex);
console.log(match);
*L'asterisco (>=0)- Indica 0 o più occorrenze. È simile al
+ma l'elemento da ricercare potrebbe anche non essere presente
// Trova tutte le i precedute o no da una o più lettere s
const testo = `Espressioni, pesi, piume`;
const regex = /s*i/g;
const match = testo.match(regex);
console.log(match);
?Il punto di domanda (1 o 0)- Indica 1 o 0 occorrenze, in pratica specifica che l'elemento potrebbe o non potrebbe essere presente
// Trova tutte le i precedute da una o da nessuna s
const testo = `Espressioni, pesi, piume`;
const regex = /s?i/g;
const match = testo.match(regex);
console.log(match);
{n}Numero racchiuso da parentesi graffe (n)- Ricercano esattamente
noccorrenze, dovenè un numero intero; Da ricordare che le parentesi graffe vengono considerate caratteri normali in tutti gli altri contesti
// Trova solamente la parola esse
const testo = `ese, esse, essse, esssse!`;
const regex = /es{2}e/g;
const match = testo.match(regex);
console.log(match);
// Cerca solamente la A racchiusa da parentesi graffe
const testo = `parola {a} parola`;
const regex = /{a}/;
const match = testo.match(regex);
console.log(match);
{n,m}Numeri separati da virgola racchiusi da parentesi graffe (da n a m)- Ricerca tra le
ne lemoccorrenze compresi, vedi sopra
// Trova solamente le parole che contengono da 2 a 4 s
const testo = `ese, esse, essse, esssse, essssse!`;
const regex = /es{2,4}e/g;
const match = testo.match(regex);
console.log(match);
{n,}Numero seguito da una virgola racchiuso da parentesi graffe (da n)- Ricerca un minimo di
noccorrenze, vedi sopra
// Trova solamente le parole che contengono 2 o più s
const testo = `ese, esse, essse, esssse, essssse!`;
const regex = /es{2,}e/g;
const match = testo.match(regex);
console.log(match);
I quantificatori ungreedy (ovvero… non golosi)
Quasi tutti inciampano prima o poi in questo problema: se utilizzo una espressione del tipo /"(.*)"/ troverò tutte le parole racchiuse tra doppi apici? Purtroppo non sempre!
Questo perché i quantificatori normali sono “golosi” (in inglese greedy), cioè cercano sempre l’occorrenza il più grande possibile.
Vediamo con un esempio:
// Voglio cercare tutte le parole racchiuse da doppi apici
const testo = `"Pluto" è un cane come "Pippo"`;
const regex = /"(.*)"/g;
const match = testo.match(regex);
console.log(match);
WTF? Come vedete non è il risultato sperato! Come fare quindi?
Basta aggiungere un ? (punto interrogativo) subito dopo i quantificatori:
// Voglio cercare tutte le parole racchiuse da doppi apici
const testo = `"Pluto" è un cane come "Pippo"`;
const regex = /"(.*?)"/g;
const match = testo.match(regex);
console.log(match);
Ovviamente questo vale per qualsiasi quantificatore descritto in precedenza!
È inoltre possibile specificare il modificatore U alla fine della nostra espressione regolare per indicare che tutti i quantificatori sono da considerarsi ungreedy, ma purtroppo non è implementato in javaScript!
Le classi e gli intervalli
Le classi determinano un elenco di caratteri, di classi di caratteri o di POSIX (vedi la sezione successiva) da ricercare. Vengono racchiusi tra parentesi quadre e possono essere seguiti dai quantificatori.
// Cerco due vocali consecutive nella stringa
const testo = `Questa è una stringa lunga lunga di esempio`;
const regex = /[aiuoe]{2}/g;
const match = testo.match(regex);
console.log(match);
Per identificare un intervallo invece si utilizza il segno - (trattino alto) . Per esempio a-z identificherà tutti i caratteri minuscoli dalla a alla z, F-R i caratteri maiuscoli dalla F alla R, 0-5 i numeri da 0 a 5 e così via.
// Cerco 6 caratteri che siano numeri o lettere dalla a alla f
const testo = `Un codice aa12bb ed un colore esadecimale 94fa3c`;
const regex = /[0-9a-f]{6}/g;
const match = testo.match(regex);
console.log(match);
Il carattere ^ posto subito dopo la parentesi quadra aperta nega tutto l’intervallo, cioè indica di non ricercare i caratteri inclusi.
// Troverà 3 caratteri minuscoli che non siano vocali
const testo = `Questa è una stringa lunga lunga di esempio`;
const regex = /[^aiuoe]{3}/g;
const match = testo.match(regex);
console.log(match);
Le classi di caratteri e i POSIX
Le classi di caratteri e i POSIX servono per specificare una serie di caratteri allo stesso tempo, senza dover scomodare gli intervalli.
Da ricordarsi che i POSIX in JavaScript non sono implementati, mentre in PHP devono essere racchiusi da una doppia parentesi quadra.
Solitamente una classe maiuscola è la negazione di quella minuscola.
\wParole- Ricerca un carattere “parola” (w sta per word), cioè lettere, numeri e
_( underscore), corrisponde a[a-zA-Z0-9_]. Si può utilizzare anche il POSIX[:word:]
// Troverà solo i caratteri word
const testo = `[[Le_Regex sono_belle!!!]]`;
const regex = /\w+/g;
const match = testo.match(regex);
console.log(match);
\WNon parole- L'opposto di
\w, cioè qualsiasi cosa che non sia una lettera, numero o “_”, corrisponde a[^a-zA-Z0-9_]
// Troverà solo i caratteri word
const testo = `[[Le_Regex sono_belle!!!]]`;
const regex = /\W+/g;
const match = testo.match(regex);
console.log(match);
\dNumeri- Ricerca un numero (d sta per digit), corrisponde a
[0-9]. Si può utilizzare anche il POSIX[:digit:]
// Troverà solo numeri
const testo = `123 stella! 456 cometa!`;
const regex = /\d+/g;
const match = testo.match(regex);
console.log(match);
\DNon numeri- L'opposto di
\d, cioè qualsiasi cosa che non sia un numero, corrisponde a[^0-9]
// Troverà tutto quello che non è un numero
const testo = `123 stella! 456 cometa!`;
const regex = /\D+/g;
const match = testo.match(regex);
console.log(match);
\sSpazi- Ricerca uno spazio, comprese tabulazioni e caratteri di fine riga, corrisponde a
[ \t\r\n\v\f]. Si può utilizzare anche il POSIX[:space:]
// Troverà solo gli spazi
const testo = `Manuale!
sulle espressioni regolari!`;
const regex = /\s+/g;
const match = testo.match(regex);
console.log(match);
\SNon spazi- L’inverso di
\s, ricerca qualsiasi cosa non sia uno spazio, una tabulazione o dei caratteri di fine riga, corrisponde a[^ \t\r\n\v\f]
// Troverà tutto ciò che è racchiuso da spazi
const testo = `Manuale!
sulle espressioni regolari!`;;
const regex = /\S+/g;
const match = testo.match(regex);
console.log(match);
\p{X}Carattere unicode- Ricerca un carattere unicode di tipo X. Da utilizzare congiuntamente al modificatore
uAlcuni dei tipi utilizzabili sono: \p{L}- Qualsiasi tipo di carattere in qualsiasi linguaggio, estendibile in:
\p{Ll}: minuscolo che ha una variante maiuscola\p{Lu}: maiuscolo che ha una variante minuscola\p{Lt}: d'inizio parola con solo l'iniziale maiuscola\p{L&}: che ha sia una variante minuscola che maiuscola (Ll+Lu+Lt)\p{Lm}: carattere speciale usato come lettera\p{Lo}: che non ha il corrispettivo maiuscolo o minuscolo
// Troverà tutte le parole in ogni lingua, anche con lettere accentate!
const testo = `Èspressioni regòlari, 正規表現, Regulärer Ausdruck, تعبير نمطي`;
const regex = /\p{L}+/gu;
const match = testo.match(regex);
console.log(match);
\p{M}- Carattere come accenti, umlaut, ecc. Estendibile in:
\p{Mn}: che si combina a un altro senza occupare uno spazio\p{Mc}: che si combina a un altro e che occupa uno spazio\p{Me}: che racchiude il carattere a cui si combina (cerchio, quadrato, ecc)
\p{Z}- Qualsiasi tipo di spazio o separatore invisibile
\p{Zs}: che occupa uno spazio\p{Zl}: separatore di linea (U+2028)\p{Zp}: separatore di paragrafo (U+2029)
// Troverà tutti gli spazi
const testo = `Le Espressioni regolari`;
const regex = /\p{Z}+/gu;
const match = testo.match(regex);
console.log(match);
\p{S}- Simboli matematici, valute, decorativi
\p{Sm}: qualsiasi simbolo matematico\p{Sc}: qualsiasi simbolo di valuta\p{Sk}: un carattere che si combina a un altro\p{So}: altri simboli che non sono matematici, di valuta o di combinazione
// Troverà solo i simboli matematici
const testo = `Il nove: 3 + 3 + 3 = 3² = √81 = Ⅸ`;
const regex = /\p{Sm}+/gu;
const match = testo.match(regex);
console.log(match);
\p{N}- Caratteri numerici
\p{Nd}: dallo 0 al 9 in qualsiasi scrittura non ideogramma\p{Nl}: che sembrano una lettera, come i numeri romani\p{No}: cifra in apice o pedice, o che non è una cifra 0-9, non ideogramma
// Troverà tutti i numeri
const testo = `Il nove: 3 + 3 + 3 = 3² = √81 = Ⅸ`;
const regex = /\p{N}+/gu;
const match = testo.match(regex);
console.log(match);
\p{P}- Caratteri di punteggiatura
\p{Pd}: qualsiasi tipo di trattino o linea\p{Ps}: qualsiasi tipo di parentesi aperta\p{Pe}: qualsiasi tipo di parentesi chiusa\p{Pi}: qualsiasi tipo di virgoletta d'inizio\p{Pf}: qualsiasi tipo di virgoletta di fine\p{Pc}: che connette più parole, tipo l'underscore\p{Po}: qualsiasi altro carattere
// Troverà le parentesi aperte e chiuse
const testo = `⧼ {[(5+1)+7]*(2+x)}=(a+b)*4 ⧽`;
const regex = /[\p{Ps}\p{Pe}]+/gu;
const match = testo.match(regex);
console.log(match);
\p{C}- Caratteri di controllo invisibili e altri caratteri
\p{Cc}: un carattere di controllo ASCII o Latin-1: 0x00–0x1F e 0x7F–0x9F\p{Cf}: indicatore di formattazione invisible\p{Co}: carattere riservato per uso privato\p{Cs}: metà di una coppia surrogata nella codifica UTF-16\p{Cn}: qualsiasi codice a cui non è stato assegnato un carattere
\p{sc=xxx}- Caratteri solo nella lingua xxx specificata, dove xxx è il codice che definisce la lingua ISO15924. Ecco alcuni esempi:
\p{sc=Latin}: un carattere latino\p{sc=Han}: un carattere cinese\p{sc=Greek}: un carattere greco\p{sc=Cyrillic}: un carattere cirillico
\p{XXX}- Altri codici, ne elencherò solo alcuni di utili
\p{Emoji_Presentation}: Cerca tutti gli emoji\p{Emoji_Modifier}: Cerca solo i caratteri che modificano l'emoji\p{ASCII}: Solo caratteri ascii
Potete trovare tutte le tipologie di codice a questo indirizzo , ma il supporto varia a seconda del linguaggio utilizzato
I seguenti POSTFIX non sono implementati in javaScript, quindi non sono presenti esempi:
[:alnum:]- Ricerca caratteri alfanumerici, senza
_. Corrisponde a[a-zA-Z0-9] [:alpha:]- Ricerca caratteri alfabetici. Corrisponde a
[a-zA-Z] [:blank:]- Ricerca solo spazi e tabulazioni. Corrisponde a
[ \t] [:lower:]- Ricerca solo lettere minuscole. Corrisponde a
[a-z] [:upper:]- Ricerca solo lettere maiuscole. Corrisponde a
[A-Z] [:graph:]- Ricerca tutti i caratteri visibili a video della tabella ASCII non estesa, dal numero 33 (!) al 126 (~). Corrisponde a
[\x21-x7E] [:print:]- Ricerca tutti i caratteri visibili a video della tabella ASCII non estesa, dal numero 32 (spazio) al 126 (~). Corrisponde a
[\x20-x7E] [:punct:]- Ricerca tutti i caratteri di punteggiatura. Corrisponde a
[\-!”#\$%&’\(\)*+,\.\\/:;<=>\?@\[\]\^_{\|}~]
I modificatori
Ogni operazione di ricerca può utilizzare vari modificatori, che, come dice il nome stesso, possono modificare i criteri di ricerca predefiniti. Questi modificatori devono essere posizionati alla fine della stringa di ricerca, subito dopo il carattere di limitazione. È possibile combinare più effetti accodando senza spazi i modificatori (per esempio: /im applicherà tutti e due gli effetti sotto descritti).
gGlobal- In JavaScript indica una ricerca “globale”, cioè non si ferma solo alla prima corrispondenza
// Troverà tutte le parole di almeno 3 lettere
const testo = `E uno, due tre e quattro!`;
const regex = /[a-zA-Z]{3,}/g;
const match = testo.match(regex);
console.log(match);
icase-Insensitive- La ricerca diventa case-insensitive, cioè maiuscole e minuscole vengono considerate uguali
// Troverà la parola "regolari" in qualsiasi forma maiuscola/minuscola
const testo = `Le Espressioni Regolari sono regolari o ReGoLaRi?`;
const regex = /regolari/gi;
const match = testo.match(regex);
console.log(match);
mMultiline- La ricerca verrà considerata “per riga”, cioè le ancore tipo ^ e $ verranno applicate per ogni riga di testo
// Troverà sole le *una* a fine riga
const testo = `Questa è una
frase su più righe e una sentenza
con molta fortuna`;
const regex = /una$/gm;
const match = testo.match(regex);
console.log(match);
sSpace- Il testo viene considerato un’unica riga e
.ora identifica anche i caratteri di fine riga, che normalmente non troverebbe
// Troverà sole le *una* a fine riga
const testo = `Questa è una
frase su più righe e una sentenza
con molta fortuna`;
const regex = /una./gs;
const match = testo.match(regex);
console.log(match);
uUnicode- Vengono abilitati i caratteri Unicode estesi. Vedi la classe
\p{X}
// Troverà solo il testo in cinese
const testo = `Espressioni regolari, 正则表达式, रेग्युलर ऍक्सप्रैशन`;
const regex = /\p{sc=Han}+/gu;
const match = testo.match(regex);
console.log(match);
UUngeedy- Attiva l’opzione ungreedy a tutti i quantificatori, non applicabile in JavaScript
Le ancore
Le ancore identificano la posizione in cui ricercare il testo.
^Inizio stringa- Identifica l’inizio della stringa; con il modificatore
/midentifica l’inizio di ogni riga
// Troverà la prima parola
const testo = `Questo è un testo
su più righe`;
const regex = /^\w+/g;
const match = testo.match(regex);
console.log(match);
$Fine stringa- Identifica la fine della stringa; con il modificatore
/midentifica la fine di ogni riga
// Troverà sl'ultima parola
const testo = `Questo è un testo
su più righe`;
const regex = /\w+$/g;
const match = testo.match(regex);
console.log(match);
\AInizio stringa reale- Similmente a
^, identifica solo l’inizio della stringa, anche se è presente il modificatore/m. Non è presente in JavaScript \ZFine stringa reale- Similmente a
$, identifica solo la fine della stringa, anche se è presente il modificatore/m. Non è presente in JavaScript \bCarattere di delimitazione parola- Identifica quel punto tra un carattere
\w([a-zA-Z0-9_]) e uno che non lo è. Il punto non ha lunghezza e può identificare anche l’inizio o la fine della stringa. \ Per esempio troverà il punto tra una lettera e un simbolo ma non il punto tra una lettera e un’altra lettera. \ È un concetto un po’ complicato, per questo è meglio chiarirlo con tre esempi
// Troverà solo le parole che iniziano per "si"
const testo = `«si si! è "simpatico" e positivo in certi casi!»`;
const regex = /\bsi/g;
const match = testo.match(regex);
console.log(match);
// Troverà solo le parole che finiscono per "si"
const testo = `«si si! è "simpatico" e positivo in certi casi!»`;
const regex = /si\b/g;
const match = testo.match(regex);
console.log(match);
// Troverà solo i "si" non all'interno di altre parole
const testo = `«si si! è "simpatico" e positivo in certi casi!»`;
const regex = /\bsi\b/g;
const match = testo.match(regex);
console.log(match);
\BCarattere di delimitazione parola... inverso- L’opposto di
\b, in pratica identifica quel punto tra due caratteri\woppure tra due caratteri non\w. Per esempio troverà il punto tra due lettere oppure il punto tra due simboli.
// Troverà solo i "si" preceduti da una lettera
const testo = `«si si! è "simpatico" e positivo in certi casi!»`;
const regex = /\Bsi/g;
const match = testo.match(regex);
console.log(match);
// Troverà solo i "si" seguiti da una lettera
const testo = `«si si! è "simpatico" e positivo in certi casi!»`;
const regex = /si\B/g;
const match = testo.match(regex);
console.log(match);
// Troverà solo i "si" all'interno di una parola
const testo = `«si si! è "simpatico" e positivo in certi casi!»`;
const regex = /\Bsi\B/g;
const match = testo.match(regex);
console.log(match);
I caratteri speciali
Chi ha un po’ di conoscenza di programmazione avrà già avuto a che fare con quei segnaposto che identificano dei caratteri speciali, come quelli di fine riga o le tabulazioni. Tali segnaposto iniziano tutti con il carattere \ (barra) Ecco un elenco molto più che dettagliato, da sottolineare l’importanza di \Q e \E, purtroppo non implementati in javaScript.
\t: Carattere di tabulazione (HT, TAB), il tasto↹sulla tastiera\n: Carattere di fine riga (LF, LN)\r: Carattere di ritorno a capo (CR)\Q: disabilita qualsiasi metacarattere presente fino a\E, molto utile per inserire delle variabili nella stringa. Non implementato in javascript\E: Vedi sopra. Non implementato in javascript\nnn: Carattere in forma ottale dove n è un numero da 0 a 7\xnn: Carattere in forma esadecimale dove n è un numero esadecimale a-z0-9\f: Carattere di Form feed (FF)\a: Carattere di alarm/bell (BEL)\e: Carattere di escape (ESC)
I gruppi
I gruppi vengono racchiusi dalle parentesi tonde e diventano essenziali nel momento della sostituzione, poiché è possibile richiamarli. Un esempio per chiarire tutto:
// Converte le date da anno-mese anno a giorno/mese/anno
const testo = `Questa è una data: 2010-01-28
e questa è un'altra data: 2022-10-22`;
const regex = /(\d{4})-(\d{2})-(\d{2})/g;
const replace = '$3/$2/$1';
console.log(testo.replace(regex, replace));
Come vedete l’espressione contiene tre gruppi e nella sostituzione compaiono dei dollari seguiti da un numero: questo numero rappresenta il testo trovato dal gruppo corrispondente. Così $1 identificherà il primo gruppo, $2 il secondo e così via. Tutta la stringa trovata verrà inserita in $0.
È possibile anche richiamare un gruppo nell'espressione regolare utilizzando \1 e \2, utile nelle ricerche di elementi racchiusi dallo stesso elemento:
// Cerca un testo racchiuso da apici ' o ", ma non da apici diversi
const testo = `->"questa la trova"
-> 'questa anche' <-
'questa no!"
"e questa neache'`;
const regex = /(['"]).*?\1/g;
const match = testo.match(regex);
console.log(match);
Molto utile è la possibilità di dare dei nomi ai gruppi, così da evitare fraintendimenti con i numeri o errori in caso di spostamenti o creazioni di nuovi gruppi. Per fare questo basta aggiungere ?<nome> subito dopo l’apertura della parentesi e richiamarli con $<nome>:
// Converte le date da anno-mese anno a giorno/mese/anno
const testo = `Questa è una data: 2010-01-28
e questa è un'altra data: 2022-10-22`;
const regex = /(?<anno>\d{4})-(?<mese>\d{2})-(?<giorno>\d{2})/g;
const replace = ' <giorno>/<mese>/<anno>';
console.log(testo.replace(regex, replace));
Nei gruppi inoltre è possibile aggiungere un’espressione logica “OR”, cioè poter ricercare una serie di caratteri oppure un’altra, separati da | (pipe):
// L'espressione cercherà "ha piovuto" oppure "è piovuto"
const testo = `In italiano si può dire sia aveva piovuto o era piovuto!`;
const regex = /\b((aveva|era) piovuto)\b/g;
const replace = ' $2 nevicato';
console.log(testo.replace(regex, replace));
Questa funzione ritornerà sia aveva piovuto che era piovuto; ma attenzione! Come notate, anche in questo caso le parentesi tonde rappresentano un gruppo e quindi verrà considerato nelle variabili di sostituzione, in questo caso $1 conterrà aveva piovuto, $2 conterrà aveva o era. Qui non sembra un problema, ma nelle espressioni più complesse ricercare cose non necessarie complica orrendamente le cose. Come fare allora? Basta utilizzare i gruppi passivi, aggiungendo un ?: subito dopo la parentesi tonda aperta:
// L'espressione cercherà "ha piovuto" oppure "è piovuto"
// senza recuperare il gruppo "aveva piovuto" e "era piovuto"
// Ora il gruppo $1 conterrà aveva/era
const testo = `In italiano si può dire sia aveva piovuto o era piovuto!`;
const regex = /\b(?:(aveva|era) piovuto)\b/g;
const replace = ' $1 nevicato';
console.log(testo.replace(regex, replace));
Le asserzioni
Ho lasciato appositamente per ultime le asserzioni, visto che la maggior parte delle persone ha molta difficoltà ad assimilarle. Però una volta imparate le userete spesso e volentieri!
Esponiamo un problema: è possibile ricercare solo quelle parole che iniziano con la lettera “c” ma che la seconda lettera non sia una vocale? Certo, basta utilizzare le asserzioni!
In pratica sono degli elementi di controllo che vengono applicati alle nostre ricerche. Inoltre tutte le asserzioni sono passive, cioè, come spiegato prima nei gruppi, non vengono aggiunte fra le variabili di ricerca.
È un po’ complicato da spiegare, ma vedrete che con gli esempi qui sotto tutto risulterà più chiaro! (ne ho messi addirittura due per tipo 😝)
(?=cond)Asserzione lookahead positiva- Asserzione lookahead positiva, cioè valida l’espressione precedente solo se la condizione cond è verificata.
// Cercherà le parole precedenti a quelle che iniziano per c, u, e
const testo = `La volpe conosce molti trucchi e l'istrice uno solo, ma buono.`;
const regex = /\w+(?= [cue])/g;
const match = testo.match(regex);
console.log(match);
Come notate le parole trovate sono solo quelle seguite da una parola che inizia per c, u ,e, ma nei gruppi trovati questa lettera non è presente! Un altro esempio utile per capirne il funzionamento è ricercare solo le parole che precedono uno spazio e capr e la parola subito successiva:
// Cercherà la parola precedente " capr" e la parola successiva
const testo = `il cane, il caprone, la mucca, il crotalo, delle piccole caprette`;
const regex = /\w+(?= capr) \w+/g;
const match = testo.match(regex);
console.log(match);
(?=cond)Asserzione lookahead negativa- Asserzione lookahead negativa, cioè valida l’espressione precedente solo se la condizione cond non è verificata.
// L'espressione cercherà la parola due seguita da qualsiasi parola
// diversa da pesi
const testo = `due pesi, due misure o due pesi e due misurazioni?`;
const regex = /due (?!pesi)\w+/g;
const match = testo.match(regex);
console.log(match);
Ricordatevi sempre che il gruppo delle asserzioni non verrà mai restituito!
// Cercherà la parola non precedente a " capr" e la parola successiva
const testo = `il cane, il caprone, la mucca, il crotalo, le caprette, il rinoceronte`;
const regex = /\w+(?! capr) \w+/g;
const match = testo.match(regex);
console.log(match);
(?<=cond)Asserzione lookbehind positiva- Asserzione lookbehind positiva, cioè valida l’espressione successiva solo se la condizione cond è verificata.
// L'espressione cercherà la parola successiva a "due "
const testo = `due pesi, due misure o due pesi e ventidue misurazioni?`;
const regex = /(?<=due )\w+/g;
const match = testo.match(regex);
console.log(match);
Se per esempio voglio trovare solo le parole con gli articoli il, lo e la mi basterà utilizzare la seguente espressione:
// L'espressione cercherà le parole con gli articoli il, lo e la
const testo = `il cane, lo scoiattolo, la mucca, gli emù nello stagno`;
const regex = /(?<=\b(?:il|lo|la) )\w+/g;
const match = testo.match(regex);
console.log(match);
Come vedete la ricerca non troverà stagno perché è presente l'ancora \b. Provate a modificarla togliendolo e vedrete la differenza! Ah, provate pure a togliere il gruppo passivo ?:
Analizziamola meglio nel dettaglio:
╔═══╗╔══╗╔═════════════╗╔═╗╔═══╗
║(?=║║\b║║(?:il|lo|la) ║║)║║\w+║
╚╤══╝╚╤═╝╚═╤═══════════╝╚╤╝╚═╤═╝
┌─────────┴──┐ │ ┌──┴──────┐ ┌──┘ │
│ Inizio │ │ │ Gruppo │ │ ┌────┴───┐
│ Asserzione │ │ │ Passivo │ │ │ Parola │
└────────────┘ │ └─────────┘ │ └────────┘
┌─────────┴────┐ ┌─────┴──────┐
│ Delimitazione│ │ Fine │
│ parola │ │ Asserzione │
└──────────────┘ └────────────┘
(?<!cond)Asserzione lookbehind negativa- Asserzione lookbehind negativa, cioè valida l’espressione successiva solo se la condizione cond non è verificata.
// Trova un numero di tre cifre che non è preceduto da £
const testo = `€500 $400 £212 #700`;
const regex = /(?<!£)\d{3}/g;
const match = testo.match(regex);
console.log(match);
// Cercherà le lettere n non precedute da vocale e le lettere seguenti
const testo = `il cane, la faina, lo gnu, gli emù nello stagno`;
const regex = /(?<![aiuoe])n\w+/g;
const match = testo.match(regex);
console.log(match);
(?>cond)Gruppo atomico- Sottoespressione indipendente, cioè l’espressione viene considerata singolarmente senza considerare altre cose. Con un esempio torna tutto più chiaro:
a(bc|b)ctroverà sia la stringa abcc (a seguita da bc seguito da c) che la stringa abc (a seguita da b seguita da c)a(?>bc|b)ctroverà invece solamente la stringa abcc in quanto in abc troverà la lettera a seguita dal gruppo atomico bc e non troverà la c finale.
Non è disponibile in javaScript ma può essere emuluta con (?=(?:cond))\1:
// Gruppo atomico in javascript
const testo = `abcc abc abcc abc abc abcc`;
const regex = /a(?=(bc|b))\1c/g;
const match = testo.match(regex);
console.log(match);
Ecco la spiegazione :
╔═══╗╔═════╗╔════════╗╔═══╗╔════╗╔═══╗ ┌─────────┐
║ a ║║ (?= ║║ (bc|b) ║║ ) ║║ \1 ║║ c ╟─┤Lettera c│
╚═╤═╝╚══╤══╝╚══╤═════╝╚═╤═╝╚═╤══╝╚═══╝ └─────────┘
┌──────────┴─┐ │ ┌──┴────┐ │ ┌─┴─────────────┐
│ Lettera a │ │ │Gruppo1│ │ │ Rif. gruppo 1 │
└────────────┘ │ └───────┘ │ └───────────────┘
┌────────────┴─────┐ ┌──┴─────────────┐
│ Inizio lookahead │ │ Fine lookahead │
│ negativa │ │ negativa │
└──────────────────┘ └────────────────┘
Conclusioni
Bene, ora avete una infarinatura sulle espressioni regolari! Per creare e testare la vostra regex potete utilizzare il servizio offerto da regex101.com, il quale vi mostra il risultato della vostra espressione al volo con pure la descrizione 🙂 (esempio)
In futuro aggiornerò questa guida aggiungendo esempi e nuove descrizioni, quindi vi consiglio di aggiungerla ai preferiti! Ovviamente se avete cose da aggiungere potete farlo nei commenti!
Commenti
Bella questa guida! E' già nei preferiti!
Salve ho un piccolo problema di lavoro ma sulla shell sono alle prome armi e avrei bisogno di un suggerimento in linux come faccio a verificare se un dato file contiene solo numeri ?? sarei molto grato grazie.
Ciao Silvio! Per la ricerca di file di soli numeri devi utilizzare il comando "find":
grazie mi chiedevo : questa sintassi mi restituisce i file con il nome in forma numerica o con il contenuto ? sarebbe tipo :
e' cosi? come controllo il singolo file ?? grazie tante
Beh, potresti invece fare una cosa del genere:
In $lista avresti tutti i file contenenti numeri presenti nella cartella 🙂
la provo subito grazie tante Chalda
Ottima guida: semplice, completa, schematica e sinottica, piena di esempi chiari e facili. Peccato per un piccola svista proprio nella parte iniziale, nell'esempio sull'uso del meta-carattere $, che rischia di disorientare i più inesperti (i cosiddetti neofiti). La versione corretta dell'esempio è ovviamente:
Questo è quello che ho trovato a colpo d'occhio, ad una prima scorsa veloce. Non escludo quindi la presenza di altri errori (magari più importanti), ma ho ritenuto di segnalartelo subito, a causa dell'impatto psicologico negativo che ha avuto su me stesso (anche se poi leggendo il seguito mi sono ricreduto). Vale quindi la pena di correggerlo al più presto! Naturalmente, se avrò tempo e voglia di analizzare a fondo tutto il documento, non mancherò di inviarti miei eventuali commenti ulteriori. Personalmente ho già una buona esperienza sull'argomento e non mi mancano certo guide e tutorial, tutti in inglese, ed anche ottima guida tascabile in italiano, trovata per caso in libreria un mese fa' a pochi euro (se ti interessa ti farò sapere i dettagli), che ancora devo leggere (spiega anche le differenze fra Perl, PHP, JavaScript, etc., con esempi). Mi sono imbattuto nel tuo sito perchè stavo cercando una guida semplice e valida in italiano per un amico (possibilmente gratuita), e credo francamente di averla trovata proprio qui. Complimenti e buon proseguimento. Valerio.
Grazie della segnalazione, ho corretto l'errore di "copiaincollatura" 🙂 In inglese ci sono molte guide più complete, io di solito mi baso su http://www.regular-expressions.info/ , però parla in modo alquanto complesso per uno che si deve avvicinare alle espressioni regolari. Grazie dei complimenti e lieto di esserti stato d'aiuto 😛
correzione: quindi troverà "ue" e "io"
Comunque grazie a questa guida sto imparando questo argomento, è veramente un casino impararli, in internet è tutto in inglese e le poche guide in italiano che ci sono partono subito in quarta e non si capisce niente. Li sto imparando meglio qui che nel librone da 900 pagine che ho! 😛
Grazie della segnalazione e dei complimenti, sono contento di esserti stato utile!
Ciao a tutti, devo costruire un'espressione regolare che mi trova delle stringe racchiuse tra parentesi graffe, al fine di eliminarle. Per esempio: la mia stringa iniziale è:
Testo bla bla bla {pippo} 2 bla bla bla {topolino} ciao ciao; quella finale deve diventareTesto bla bla bla 2 bla bla bla ciao ciao; Qual è l'esatta espressione regolare da utilizzare nella funzioneeregi_replace?Allora... ti sconsiglio di utilizzare eregi, in quanto è deprecato ( http://www.php.net/manual/en/function.ereg.php ), meglio usare (
preg_replace)http://www.php.net/manual/en/function.preg-replace.php! Puoi comunque utilizzare la seguente espressione:Ereg non supporta gli ungreedy, quindi la sintassi è un attimo diversa. Comunque ti rinnovo il suggerimento di dimenticare ereg per passare a preg 😛
Bravissimo, guide così se ne trovano davvero poche in giro! Ti segnalo un errore di copiatura nell'asserzione lookbehind negativa. Hai copiato
Mentre dovrebbe essere
Ancora complimenti, Luca Bartoli
Io mi sono letto tutta la guida ma c'è una cosa che non riesco proprio a fare Mettiamo il caso di questo esempio Io ho una stringa lunghissima dove ci sono molte parole tipo "details" Vorrei che il preg_match trovasse tutto il testo che sta tra ogni "details" e il più vicino punto "." Esempio
Mi sto dannando con questo preg_match e non riesco proprio a cavarne piede Ringrazio tantissimo a chi mi dà una mano in questo! Ciao grazie!
Ciao Alessio, La cosa è semplice:
In pratica ricerchi tutto quello che c'è tra details ed un punto. Fai attenzione al ? (che identifica la ricerca ungreedy) e l'escape (\) prima del punto
ciao... innanzitutto complimenti per la guida... avrei una domanda: mettiamo che abbia la stringa:
come dovrei scrivere l'espressione regolare che mi dia true confrontandola con la stringa
ti ringrazio comunque ma ho già risolto... io scrivevo l'espressione correttamente... ma misteriosamente non funzionava... forse qualche virgola fuoriposto... ancora complimenti per la guida!
ciao, complimenti per la guida, tuttavia non ho ben capito come fare per le lettere accentate. devo controllare un nome, quindi voglio accettare lettere, lettere accentate, apostrofi e spazio. ho provato in questa maniera, utilizzando i codici ASCII, ma non funziona:
hai qualche idea? grazie mille
Complimenti per la guida mi è stata molto utile. Posso porti un quesito? Ho un problema che mi sta facendo impazzire ma sono certo che mi sto perdendo in un bicchiere d'acqua.
Ciao, complimenti per la guida. Vorrei farti una domanda: è possibile utilizzare le espressioni regolari per rimpicciolire o ingrandire una immagine senza ricorrere alle librerie GD? Se sì, come? In dettaglio: con una query a mysql recupero dei valori che sono testo e immagine con tutti i tag del caso. L'immagine nel tag html ha i width e l'height valori predeterminati. Una volta che recupero il testo e l'immagine vorrei che quest' ultima assuma dei valori width e l'height diversi.
Ciao! Grazie per questo! Questo mi ha aiutato molto 🙂 Alex
Ciao a tutti, vi chiedo un consiglio... devo ricercare del testo in una pagina html tipo @aaa bbb ccc@ oppure @aaa bbb@ devo trasformarlo in aaa bbb ccc (nel primo caso) oppure aaa bbb Non ci salto fuori 🙂
Ciao Marco, suppongo che tu indenda in php... Per rimpiazzare il testo pasta che tu utilizzi preg_replace:
Ciao, sto eseguendo un url-rewrite tramite il file .htaccess con il seguente codice
^news/([a-z._-]+)/([0-9]+).html$Ora devo implementare l'espressioni regolari permettendo gli " e caratteri speciali come posso fare? Vi ringrazio in anticipo 🙂Ciao Simone, Per i caratteri speciali è sufficiente aggiungere un backslash (\) prima. Per esempio
*diventa\*. CiaoCiao, e grazie per la guida. E' possibile unire più espressioni come
[:BLANK:]e[:punct:]grazie di nuovoCiao Hurry, Certo, è possibile utilizzare quante più classi si vogliono. Basta utilizzarle seguendo la sintassi corretta! 😛
ciao, mai vista una guida cosi chiara e precisa come questa. credo che verrò più spesso a trovarti 🙂
Fantastico 🙂 Avevo bisogno di creare un tag personalizzato per contenere facilmente un parametro di un indirizzo Web, e il testo di quell'indirizzo... Qualcosa di simile:
Da trasformare in:
Ci ho impiegato 15 minuti a capire da zero come si fa... Grazie per l'aiuto 🙂 Ecco la mia soluzione:
Ciao ho tanti file in xml dove devo eliminare del testo composto da attributo più data. Di solito uso notepad++ ma in questo caso la data non sempre è la stessa e credo sia necessario usare le espressione regolari. La stringa che devo eliminare è:
datavig="20021001″Quale espressione regolare devo utilizzare per i numeri? Grazie e complimenti morena Grazie
Ciao Morena, l'espressione semplificata è questa:
datavig='[\d]{8}'Ciao, ChaldaCiao mi sa che mi sono complicato la vita... è la prima volta che affronto questo tipo di espressione. Questo è il mio pattern: ho dei nomi presi da un db e non voglio far visualizzare tutta la stringa che ha prefisso
[Migra_]o[migra_], ho cominciato ad applicare e.. mi sono perso! Qualcuno può aiutarmi? GrazieCiao Luca, In questo caso ti conviene direttamente filtrare i risultati tramite query, aggiungendo una clausola where
NOT LIKE 'migra%'? Ciao, ChaldaCiao grazie della risposta ma la query è una query ldap e mi ritorna un array e non so se si può applicare la clausola sql, quindi avevo pensati di filtarre l'array. luca
Allora dovresti utilizzare qualcosa del tipo:
In questo modo nell'array $lista_filtrata avresti tutte le righe che non iniziano per migra o Migra.
Grazie! ho applicato il filtro e... prometto che studierò di più!
Guida inserita tra i preferiti... anche se nel mio caso, a parte la sintassi, avrei bisogno di maggiori esempi pratici. Nello specifico ho bisogno di poter ricercare (e solo eventualmente modificare) del testo che si trova in una specifica colonna e/o in un range specifico di colonne. Testo di esempio:
1234567890 1234567890 1234567890 1234567890 ...Risultato da ottenere
1234EF890 1234EF890 1234EF890 1234EF890 ...Con editors quali SPF è possibile dare questo comando: C "56" EF" 5 6 ALL (ossia Change stringa1 con stringa2 tra le posizioni 5 e 6 del testo... tutte le occorrenze ... semplice vero?). Ovviamente dovrebbe essere possibile sostituire anche con stringhe di diversa dimensione... es 56 con PIPPO o con 0 etc.
Visto che Notepad++ NON mette a disposizione un campo "position" o "column" o "range" durante la ricerca del testo, l'unica è ricorrere alle espressioni regolari... sempreché siano "comode" allo scopo. Si può avere un esempio specifico per questi casi ? Sarebbe bello se eventualmente venisse scritto un plugin di ricerca "avanzata" che consenta appunto di "generare" ed "eseguire" l'espressione regolare di ricerca in base ai parametri indicati... sarebbe più alla portata di qualsiasi utente (bisogna ammettere che le espressioni regolari non sono poi tanto "intuitive"). Grazie.
Ciao Roberto, se il testo da modificare è tutto uguale e nella stessa posizione la cosa più semplice da fare con notepad++ è selezionare tutte le righe con il mouse tenendo premuto il tasto maiuscolo e poi editarle tutte assieme 🙂 Questa funzionalità è utilissima ma non è nota a molti 🙂
Ciao chalda, in realtà quella funzionalità la conosco benissimo e la utilizzo ( ma solo quando è possibile farlo), come spiegato però vorrei poter RICERCARE del testo in una specifica posizione (colonna) e/o in un range (es: da pos. 6 a pos 15 e SOLO quando viene trovata contarla e/o sostituirla con altro testo. Capirai che se devo sostituire ad esempio PIPPO con PLUTO ho tre righe così:
12345PIPPO12345 123456789012345 1234PIPPO012345 PIPPO6789012345 123456PIPPO2345E volessi sostituire PIPPO con PLUTO ma SOLO se è presente nel range di colonne 6-15 dovrei poter ottenere questo:
12345PLUTO12345 123456789012345 1234PIPPO012345 PIPPO6789012345 123456PLUTO1234Come fai a fare una cosa del genere con mouse e tasto maiuscolo? (O forse sarebbe meglio di re tasto ALT). Sarebbe stato utile anche poter selezionare il testo nel range di colonne desiderato e poi richiedere la find o la sostituzione "nella selezione", ma notepad++ NON lo consente.
Da qui l'esigenza di comprendere bene l'utilizzo delle espressioni regolari... che potrebbero essere l'unica alternativa, ma anche se potentissime, NON sono l'ideale per poter "favorire" l'utilizzo di notepad++ o altri editor da parte di utenti meno smaliziati (e pensa che io non sono tra gli utenti meno smaliziati 🙂 ).
Tu riesci a fare un conteggio ed eventualmente una sostituzione di quei casi che ti ho fatto vedere ? Ovviamente sarebbe stato TANTO MEGLIO aggiungere alla schermata di cerca o sostituisci dell'editor, anche una casella Da Posizione a Posizione... molto più inuitivo non trovi? Ciao e grazie.
La guida è fatta bene, complimenti solo che i modificatori, i quantificatori etc.. compaiono sempre in maiuscolo. Questo perchè nel css c'è l'istruzione " text-transform: uppercase;" e quindi solo dal sorgente è possibile capire se sono maiuscole o minuscole. Es. \B indentifica il punto tra due caratteri che siano \w a sinistra e non \w a destra \B identifica l'opposto di \b Vedi esempio »
Ti vorrei fare i miei complimenti !! Finalmente ho trovato una guida molto chiara alle espressioni regolari, che forse comincio a capire per la prima volta. Grazie !!
la guida se non sbaglio ha un errore: il quantificatore * ritorna anche la "i" di espressioni
ho dato una rapida occhiata all'argomento e senza nemmeno averlo letto inizio a ringraziarti 🙂 Dopo la lettura e "l'apprendimento" ovviamente rinnoverò i ringraziamenti 🙂 Pol
Ottima guida, potrebbe servirmi per il lavoro! Grazie mille.
Spiegazione molto utile e chiara. Per questo ho deciso che vale la pena contribuire per migliorarla: \W corrisponde a
[^a-zA-Z0-9_](in pratica nella tabella manca ^ all'inizio)Ottimo, grazie!
Fantastica guida, chiara, precisa ed efficace. Grazie!
Non ho ben capito l'ancora \b qualcuno me la puo spiegare
Lascia un commento