Guida al Visual Basic .NET
Capitolo 92° - Magie con le stringhe
Di tutti i tipi disponibili nel Framework .Net, sicuramente le stringhe costituiscono quello più potente, flessibile, versatile e utile.
Saper lavorare cone le stringhe, massimizzare il programma, ridurre i tempi di elaborazione, produrre risultati migliori è davvero un'"arte",
se così si può dire. Non sempre i programmatori scelgono la via più giusta: ecco una panoramica delle operazioni su stringa. System.String
Il tipo che espone le stringhe è System.String ed essendo un tipo reference, dispone di tre costruttori in overload: il primo accetta un carattere e un intero X e genera di conseguenza una stringa formata da quel carattere ripetuto X volte; la seconda accetta un array di valori Char, che vengono convertiti in un unico dato string; la terza è un arricchimento della seconda che permette di specificare anche l'indice da cui partire e il numero di caratteri da prelevare. Non sempre il programmatore ne è a conoscenza, poichè la classica dichiarazione di una variabile di questo tipo è "Dim S As String", dalla quale non si evince con evidenza la natura di oggetto della stringa. Per essere precisi esse sono oggetti immutabili, checchè se ne pensi: infatti una volta assegnatovi un valore non c'è modo di modificarlo. Questo potrebbe sembrare strano, dato che le assegnazioni non hanno mai prodotto problemi di sorta, ma in realtà non lo è. La spiegazione è semplice: quando si assegna un valore a un oggetto stringa, si crea implicitamente un nuovo oggetto stringa e si passa il puntatore ad esso alla variabile in assegnazione: 'Oggetto stringa inizialmente uguale a Nothing Dim S As String 'Crea un nuovo oggetto stringa "Ciao" e lo assegna a S S = "Ciao" E lo stesso avviene quando si usa l'operatore di concatenazione &: tutti i valori stringa intercalati dall'operatore sono nuovi oggetti creati al momento dal programma. Per questo motivo l'approccio della creazione di stringhe con l'uso dell'operatore è sconsigliabile, a favore invece della funzione Format, come si vedrà da questo elenco di metodi e proprietà:
Tutti i metodi non statici elencati, bisogna ricordarlo, sono funzioni d'istanza, quindi restituiscono un valore, ossia una stringa nuova ottenuta modificando quella esistente. Risulta infatti evidente che, siccome le stringhe sono oggetti immutabili, non possano essere modificate. Perciò il seguente codice non produrrà alcuna modificazione: Dim S As String = "Ciao" 'Rimuove il primo carattere dalla stringa, ma il valore 'restituito viene perso in quanto non c'è 'alcuna assegnazione S.Remove(0, 1) Console.WriteLine(S) '> Ciao Mentre questo otterrà il risultato: Dim S As String = "Ciao" 'Rimuove il primo carattere dalla stringa, e pone il 'riferimento alla nuova stringa creata in S S = S.Remove(0, 1) Console.WriteLine(S) '> iao
Nei frammenti di codice in cui si generano stringhe, tuttavia, è molto meglio utilizzare uno StringBuilder. System.Text.StringBuilder
Questo oggetto ha il compito ci costruire pezzo per pezzo una stringa: è progettato in modo da lavorare con singoli caratteri alla volta, così da non creare alcun stringa temporanea in memoria, risparmiando molto spazio e molto tempo. Espone alcuni metodi presi da String, come Insert e Remove. Ha una proprietà caratteristica denominata Capacity, che indica il numero massimo di caratteri contenibili nell'oggetto, correlata di funzione EnsureCapacity che si assicura che questa condizione venga rispettata: durante il lavoro, se la lunghezza della stringa in elaborazione risulta maggiore della capacità, comunque, quest'ultima viene aumentata in modo da aderire alle nuove necessità di spazio, eliminando quindi ogni problema. Le procedure importanti sono Append, che accoda una stringa al tutto, AppendLine, che inoltre manda anche a capo e AppendFormat, che aggiunge un valore formattato con una stringa di formato, come al solito. Per provare l'efficacia di StringBuilder, ecco un test a prova di bomba che calcola le prestazioni di entrambi gli utilizzi: Module Module1 Sub Main() Dim T As New Stopwatch Dim S As String = "" Dim B As New StringBuilder 'Cronometra quando si impiega a concatenare 100'000 'caratteri con l'operatore & T.Start() For I As Int32 = 1 To 100000 S &= "c" Next T.Stop() Console.WriteLine("Operatore & : {0}ms", T.ElapsedMilliseconds) 'Cronometra l'operazione con StringBuilder T.Reset() T.Start() For I As Int32 = 1 To 100000 B.Append("c") Next T.Stop() Console.WriteLine("StringBuiler : {0}ms", T.ElapsedMilliseconds) Console.ReadKey() End Sub End Module
Sul mio computer, la prima versione impiega 14'678ms, mentre la seconda 4ms... Sembra sbalorditivo, ma StringBuilder, in questo caso è quasi
4000 volte più veloce. Guardando il consumo di RAM, si noterà inoltre che la prima versione spreca in media 2000 bytes in più di memoria. System.Security.SecureString
Quando si memorizzano password o numeri importanti come quelli della carta di credito, le stringhe ordinarie perdono una quantità esagerata di punti in sicurezza. Infatti, oltre a essere reperibili nello spazio di memoria che il programma utilizza (anche se ottenere tali indirizzi è davvero assai difficile), spesso vengono salvate in file di sistema temporanei, più accessibili, oppure permangono in diverse copie nella memoria poichè, essendo immutabili, non se ne può veramente azzerare il valore. SecureString è un tipo sicuro che consente di evitare questi rischi: non presenta alcun costruttore, non è deducibile da una stringa ordinaria (altrimenti non avrebbe senso), costruisce la stringa sicura un carattere alla volta usando un algoritmo di criptazione per mascherare il carattere. Presenta metodi simili a StringBuilder, ma che lavorano solo con un carattere alla volta. Implementare una textbox che legga una password ad esempio, può essere un compito molto semplice: la stringa non viene memorizzata nel controllo, che espone un testo omogeneo, ma nell'oggetto SecureString: l'unico modo per costruire una stringa in questo modo è un carattere per volta utilizzando l'evento keypress. Dim Password As New SecureString Private Sub TextBox1_KeyPress(ByVal sender As Object, _ ByVal e As KeyPressEventArgs) Handles TextBox1.Click Select Case Asc(e.KeyChar) Case 8 'Il carattere 8 corrisponde al backspace: cancella 'l'ultimo carattere If TextBox1.SelectionStart > 0 Then Password.RemoveAt(TextBox1.SelectionStart - 1) End If '32 rappresenta uno spazio, ed è il primo carattere 'intelleggibile nella tabella ASCII. In caso sia maggiore 'o uguale a 32, quindi, il carattere non è di controllo Case Is >= 32 'Se il cursore non si trova alla fine della stringa, 'inserisce il carattere all'indice dato If TextBox1.SelectionStart < TextBox1.Text.Length - 1 Then Password.INSERT IGNOREAt(TextBox1.SelectionStart, e.KeyChar) Else Password.AppendChar(e.KeyChar) End If 'Nella textbox, visualizza * e.KeyChar = "*" End Select End Sub Un'alternativa a questo metodo non è data dall'uso della proprietà PasswordChar della textbox, poichè essa si limita a mascherare esteriormente il contenuto, che invece permane immutato nella memoria. L'unico modo per riprendere i dati così immessi nell'oggetto SecureString è utilizzare questo codice, che converte la password in un puntatore a stringa binaria e successivamente dereferenzia tale puntatore per ottenere il valore originario: 'Ottiene il puntatore a stringa BSTR. La sigla indica una Basic 'STRing alias Binary STRing. Le caratteristiche di questa 'stringa risiedono nelle modalità di memorizzazione 'sottoforma di dati nella memoria. È composta da un prefisso 'che ne specifica la lunghezza, un contenuto e un terminatore, 'noto come NULL terminator ai programmatori C. 'Non procedo oltre nell'argomento, poichè non è scopo 'del capitolo trattare questo tipo di stringhe Dim Ptr As IntPtr = Marshal.SecureStringToBSTR(Password) 'Dereferenzia il puntatore e ottiene la password vera Dim Pass As String = Marshal.PtrToStringBSTR(Ptr) 'In questa parte viene usato il valore della stringa 'Quindi il suo spazio di memoria viene istantaneamente liberato per 'evitare che possa essere rintracciata in qualche modo Marshal.ZeroFreeBSTR(Ptr) Marshal è una classe appartenente al Namespace System.Runtime.InteropServices e serve per copiare e manipolare blocchi di memoria a livello molto basso, senza riguardo nè per il tipo che per la compatbilità, ma solo per indirizzi e dimensioni. È il wrapper managed del corrispondente metodo unmanaged CopyMemory della libreria kernel32.dll di Windows. Neppure questo argomento verrà trattato oltre in questo capitolo, ma vi rimando alla lezione corrispondente sul pinvoke.
C#, TypeScript, java, php, EcmaScript (JavaScript), Spring, Hibernate, React, SASS/LESS, jade, python, scikit, node.js, redux, postgres, keras, kubernetes, docker, hexo, etc...
|