TOC

This article is currently in the process of being translated into Italian (~99% done).

Regular Expressions (Regex):

Searching with the Regex Class

Come abbiamo discusso nell'articolo precedente, le Regular Expressions consentono di definire modelli di ricerca per lavorare con le stringhe. Per elaborare questo modello di ricerca, il framework .NET dispone di una classe molto versatile: La classe Regex. In questo articolo definiremo alcuni modelli di ricerca e li utilizzeremo con la classe Regex, ma tenete presente che la sintassi delle Regular Expressions può essere piuttosto complicata e che questo è un tutorial su C# e non su Regex. Utilizzerò invece alcuni semplici schemi Regex per dimostrare come lavorare con essi in C#. Se volete saperne di più sulle espressioni regolari, vi consiglio questo tutorial sulle Regular Expressions.

Il metodo IsMatch()

In questo primo esempio, utilizzerò uno dei metodi più elementari della classe Regex, chiamato IsMatch. Esso restituisce semplicemente true o false, a seconda che nella stringa di prova siano state trovate una o più corrispondenze:

string testString = "John Doe, 42 years";
Regex regex = new Regex("[0-9]+");
if (regex.IsMatch(testString))
    Console.WriteLine("String contains numbers!");
else
    Console.WriteLine("String does NOT contain numbers!");

Si definisce una stringa di prova e si crea un'istanza della classe Regex. Passiamo la Regular Expression vera e propria come una stringa; in questo caso, la regex specifica che stiamo cercando un numero di qualsiasi lunghezza. Viene quindi emessa una riga di testo, a seconda che la regex corrisponda o meno alla nostra stringa di prova. Molto bello, ma nella maggior parte dei casi si vuole fare qualcosa con le corrispondenze: per questo c'è la classe Match.

La classe ed il metodo Match

Nel prossimo esempio, acquisiremo il numero trovato nella stringa di test e lo presenteremo all'utente, invece di limitarci a verificare che sia presente:

string testString = "John Doe, 42 years";
Regex regex = new Regex("[0-9]+");
Match match = regex.Match(testString);
if (match.Success)
    Console.WriteLine("Number found: " + match.Value);

Utilizziamo la stessa regex e la stessa stringa di test. Richiamo il metodo Match(), che restituirà un'istanza della classe Match, indipendentemente dal fatto che sia stata trovata o meno una corrispondenza. Per assicurarsi che sia stata trovata una corrispondenza, si controlla la proprietà Success. Una volta accertato che è stata trovata una corrispondenza, utilizzo la proprietà Value per recuperarla.

La classe Match contiene informazioni più utili rispetto alla sola stringa abbinata: ad esempio, è possibile scoprire facilmente dove è stata trovata la corrispondenza, quanto è lunga e così via:

string testString = "John Doe, 42 years";
Regex regex = new Regex("[0-9]+");
Match match = regex.Match(testString);
if (match.Success)
    Console.WriteLine("Match found at index " + match.Index + ". Length: " + match.Length);

Le proprietà Index e Length sono utilizzate per visualizzare informazioni sulla posizione e sulla lunghezza della corrispondenza.

Gruppi di acquisizione

Nei primi due esempi, abbiamo trovato solo un singolo valore nella nostra stringa di ricerca, ma le Regular Expressions possono, ovviamente, fare molto di più! Per esempio, possiamo trovare sia il nome che l'età nella nostra stringa di prova, eliminando al contempo le cose irrilevanti come la virgola e il testo "years". Fare cose del genere è un gioco da ragazzi per le Regular Expressions, ma se non si ha familiarità con la sintassi, potrebbe sembrare molto complicato, ma proviamo lo stesso:

string testString = "John Doe, 42 years";
Regex regex = new Regex(@"^([^,]+),\s([0-9]+)");
Match match = regex.Match(testString);
if (match.Success)
    Console.WriteLine("Name: " + match.Groups[1].Value + ". Age: " + match.Groups[2].Value);

Ho modificato la regex in modo che cerchi tutto ciò che NON è una virgola - questo valore viene inserito nel primo gruppo di cattura, grazie alle parentesi circostanti. Quindi cerca la virgola di separazione e poi un numero, che viene inserito nel secondo gruppo di cattura (sempre grazie alle parentesi circostanti). Nell'ultima riga, utilizzo la proprietà Groups per accedere ai gruppi abbinati. Uso l'indice 1 per il nome e il 2 per l'età, poiché segue l'ordine in cui i gruppi di corrispondenza sono stati definiti nella stringa regex (l'indice 0 contiene l'intera corrispondenza).

Gruppi di acquisizione identificati

Quando la regex diventa più avanzata/lunga di quella appena usata, i gruppi di cattura numerati potrebbero diventare ingestibili, perché bisogna sempre ricordare l'ordine e l'indice di questi gruppi. Fortunatamente, le espressioni regolari e il framework .NET supportano i gruppi di cattura con nome, il che consente di assegnare a ciascun gruppo un nome nella regex e di fare riferimento ad esso nella proprietà Groups. Date un'occhiata a questo esempio riscritto, in cui utilizziamo gruppi denominati anziché numerati:

string testString = "John Doe, 42 years";
Regex regex = new Regex(@"^(?<name>[^,]+),\s(?<age>[0-9]+)");
Match match = regex.Match(testString);
if (match.Success)
    Console.WriteLine("Name: " + match.Groups["name"].Value + ". Age: " + match.Groups["age"].Value);

Funziona esattamente come prima, ma ora si possono usare i nomi logici per cercare i valori corrispondenti, invece di dover ricordare l'indice corretto. Questa potrebbe non essere una grande differenza nel nostro semplice esempio, ma come già detto la si apprezzerà sicuramente quando le Regular Expressions diventeranno più complesse e lunghe.

La classe MatchCollection

La classe Match è la soluzione ideale se si vuole lavorare con una sola corrispondenza (ricordiamo che una corrispondenza può contenere più valori, come abbiamo visto negli esempi precedenti), ma a volte si vuole lavorare con più corrispondenze contemporaneamente. Per questo, abbiamo il metodo Matches() , che restituirà una classe MatchCollection. Questa conterrà tutti i valori corrispondenti, nell'ordine in cui sono stati trovati. Vediamo come può essere utilizzato:

string testString = "123-456-789-0";
Regex regex = new Regex(@"([0-9]+)");
MatchCollection matchCollection = regex.Matches(testString);
foreach (Match match in matchCollection)
    Console.WriteLine("Number found at index " + match.Index + ": " + match.Value);

Ho modificato la regex e la stringa di test rispetto agli esempi precedenti. Ora abbiamo una stringa di prova che contiene diversi numeri e una regex che cerca specificamente le stringhe composte da uno o più numeri. Utilizziamo il metodo Matches() per ottenere una MatchCollection dalla nostra regex, che contiene le corrispondenze trovate nella stringa. In questo caso, ci sono quattro corrispondenze, che vengono emesse una dopo l'altra con un ciclo foreach. Il risultato sarà simile a questo:

Number found at index 0: 123
Number found at index 4: 456
Number found at index 8: 789
Number found at index 12: 0

Se non fossero state trovate corrispondenze, sarebbe stata restituita una MatchCollection vuota.

Riepilogo

Con l'aiuto della classe Regex, insieme alle classi Match e MatchCollection, possiamo facilmente eseguire una corrispondenza di stringhe molto avanzata. La sintassi delle Regular Expressions può sembrare molto complessa, ma una volta imparata si avrà a disposizione uno strumento molto potente. Anche se non si vuole investire tempo nell'apprendimento della sintassi delle regex, spesso è possibile trovare espressioni per esigenze specifiche, create da altri programmatori, con una semplice ricerca su Google. Non appena avrete scritto o preso in prestito la stringa regex, potrete utilizzarla per i vostri scopi con le tecniche e le classi dimostrate in questo articolo.

Ma la ricerca è solo una parte del divertimento: con le espressioni regolari si possono effettuare anche operazioni di ricerca/sostituzione molto interessanti. Ne parleremo in uno dei prossimi articoli.


This article has been fully translated into the following languages: Is your preferred language not on the list? Click here to help us translate this article into your language!