Dopo aver conosciuto le stringhe come tipologia di dato in Rust, è il momento di iniziare ad imparare a lavorarci. Che si tratti di notizie, testi da analizzare o e-mail, una grandissima percentuale del contenuto con cui avremo a che fare sarà conservato in stringhe e molto spesso sarà nostro compito analizzarle. Avremo a disposizione librerie e strumenti più avanzati in tanti casi ma comunque sarà fondamentale familiarizzare con le principali funzioni che questo tipo di elementi mettono a disposizione. Ecco alcune delle più comuni funzionalità, molte delle quali useremo nel prosieguo della lezione in esempi pratici:
find
: individuare la posizione di una sottostringa all'interno di una stringa;get
: recupera una porzione di stringa a partire da una determinata posizione;replace
: sostituzione di caratteri;split
: separazione di una stringa in più elementi in base ad un separatore indicato da noi;to_lowercase
: converte tutta la stringa in minuscolo;to_uppercase
: converte tutta la stringa in maiuscolo;len
: restituisce la lunghezza della stringa.
Oltre a ciò, ci saranno due strumenti fondamentali visti in precedenza da saper utilizzare: le slice che offrono una sorta di view su una specifica parte di un vettore ma sono usatissime nello stesso ruolo con le stringhe; Option
che servirà a specificare i tipi di dato di risultati eventualmente restituiti da una funzione.
Qualche esempio di elaborazione delle stringhe in Rust
Tutti gli esempi che proporremo sono casi indipendenti che, singolarmente, potranno pertanto essere copiati nel main di un nuovo programma per essere provati e ampliati.
Iniziamo ad usare find
per vedere in quale posizione di una stringa, si trova eventualmente, una determinata sottostringa. In questo caso, cerchiamo la parola Grazie all'interno della stringa messaggio
:
let messaggio="Ciao! Grazie per averci contattato!";
let da_cercare="Grazie";
let posizione_grazie=messaggio.find(da_cercare);
if posizione_grazie.is_some(){
println!("La stringa \"{}\" si trova in posizione: {}", da_cercare, posizione_grazie.unwrap());}
else{
println!("La stringa \"{}\" non è presente nel messaggio", da_cercare);
}
La risposta che otterremo sarà:
La stringa "Grazie" si trova in posizione: 6
Notiamo che non è detto che una sottostringa sia presente all'interno di una stringa pertanto il risultato sarà custodito in una enum Option
e potrà essere letto solo in caso in cui non sia None
e dovrà essere estratto con unwrap
.
E se invece la stringa da cercare non avesse gli stessi caratteri maiuscoli o minuscoli della sua presenza nel messaggio? Se volessimo compiere una ricerca case insensitive dovremmo convertire in minuscolo sia il messaggio che il testo da cercare: premettiamo che non è l'unico modo per agire ma qui ci stiamo allenando con le funzioni sulle stringhe.
Per farlo rimoduliamo l'esempio in questo modo:
let messaggio="Ciao! Grazie per averci contattato!";
let da_cercare="GrAZie";
let posizione_grazie=messaggio.to_lowercase().find(&da_cercare.to_lowercase());
if posizione_grazie.is_some(){
println!("La stringa \"{}\" si trova in posizione: {}", da_cercare, posizione_grazie.unwrap());}
else{
println!("La stringa \"{}\" non è presente nel messaggio", da_cercare);
}
Messaggio in output:
La stringa "GrAZie" si trova in posizione: 6
I cambiamenti hanno riguardato la porzione
messaggio.to_lowercase().find(&da_cercare.to_lowercase())
in cui abbiamo applicato la funzione to_lowercase
a messaggio e stringa da cercare.
Una volta a conoscenza della posizione di qualcosa che ci interessa in una stringa potremo estrarne una porzione usando gli slice applicati alle stringhe. Il meccanismo principale per attuare un'operazione di lettura su una porzione di testo. Aggiungendo la seguente istruzione nel blocco if
:
println!("Stampiamo da {} in poi: {}",da_cercare, &messaggio[posizione_grazie.unwrap()..]);
otterremo:
Stampiamo da GrAZie in poi: Grazie per averci contattato!
Suddividere una stringa
A questo punto, poniamoci altri obiettivi. Diciamo che vogliamo suddividere la stringa in un elenco di parole singole, tutte maiuscole, in cui non appaiano i punti esclamativi:
let messaggio="Ciao! Grazie per averci contattato!";
let messaggio_senza_esclamativi=messaggio.replace("!","").to_uppercase();
let porzioni= messaggio_senza_esclamativi.split(" ");
for parola in porzioni {
println!("{}", parola);
}
Abbiamo svolto le operazioni richieste, rispettivamente, con le funzioni: split
, to_uppercase
e replace
. Il risultato ottenuto è il seguente:
CIAO
GRAZIE
PER
AVERCI
CONTATTATO
Infine, ricordiamo che Rust supporta Unicode. Pertanto potremo stampare caratteri di ogni alfabeto, simboli ed emoji, conoscendone il relativo codice. L'istruzione successiva, ad esempio:
println!("Rust è decisamente divertente \u{1F923} ... \u{270C} \u{1F605}");
equivale a:
Rust è decisamente divertente 🤣 ... ✌ 😅