A. Veneziani - Alcune considerazioni su elaborazione con stringhe in C#
←
→
Trascrizione del contenuto della pagina
Se il tuo browser non visualizza correttamente la pagina, ti preghiamo di leggere il contenuto della pagina quaggiù
A. Veneziani - Alcune considerazioni su elaborazione con stringhe in C# Importanza delle stringhe Nella elaborazione con il calcolatore due sono le grandi famiglie di tipi di dato: i numerici e gli alfanumerici. Necessità vuole quindi che, se un dato non sia opportunamente rappresentabile in modo adeguato con tipi numerici, si debba pensare per esso ad una rappresentazione alfanumerica. Quando si parla di dato alfanumerico esso può essere rappresentato come singoli caratteri (poco pratici per poter rappresentare serie di dati), vettori di char (ossia sequenze di caratteri), o stringhe. I vettori di caratteri possono essere considerati una soluzione per rappresentare alcuni dati alfanumerici, ma in realtà anch’essi soffrono talora di alcune limitazioni operative. Spesso quando si considera dati di tipo alfanumerico che non abbiano lunghezza unitaria, la soluzione più frequente ed ovvia è utilizzare un dato di tipo stringa, tipologia di variabile di cui C# è dotato (come del resto tutti i linguaggi più moderni, quali Basic, C++, Object Pascal). Stringhe La stringa in C#, come del resto in altri linguaggi, non è altro che una serie di lunghezza variabile di caratteri, eventualmente contenente spazi o persino caratteri di ritorno a capo o altri caratteri speciali. Su tali stringhe è possibile compiere alcune operazioni atte a rendere facile e immediato il processare e rielaborare dati alfanumerici. Una delle caratteristiche peculiari delle stringhe è di non avere una ampiezza predefinita, quindi la loro lunghezza è definita al momento e del tutto dinamica. Si può dire che esse siano variabili che adattano la loro ampiezza al loro contenuto. Dichiarazione di variabili stringa Le variabili stringa in C# sono dichiarate tramite la dichiarazione string: string s; essa dichiara una stringa di nome s. Una variabile stringa non essendo considerata una variabile di tipo base (quali ad esempio bool, char, int, ecc.) si comporta a tutti gli effetti una variabile oggetto, e all’inizio della sua esistenza essa ha il valore null (ossia è non istanziata). Più intuitivamente si può dire che alla variabile s non è stato mai assegnato alcun contenuto e quindi essa risulta non assegnata (unassigned). Per utilizzare la variabile s, è necessario in C#, come per gli altri tipi di variabili, darle un valore. Una variabile stringa può essere inizializzata, come altre al momento della sua dichiarazione: string s = “paperino”; Tipicamente per assegnare qualcosa alla variabile s, si deve utilizzare l’operazione di assegnazione. Si può assegnare un qualche valore ad s assegnando una costante stringa o passando il valore contenuto in un'altra variabile stringa oppure concatenando due stringhe. s = “pippo”; oppure s = s2; // ove s2 è a sua volta una variabile string oppure s = “pippo” + s2; che concatena (antepone) la stringa “pippo” al contenuto della stringa s2. Anche per questo tipo di variabili è possibile effettuare una assegnazione nel momento stesso che la variabile sia dichiarata, ossia: string s = “pluto”; in analogia con quanto è possibile con variabili di altro tipo. Pagina 1
Stringa nulla Per stringa nulla si intende una stringa senza alcun carattere. Come logico il motivo della presenza di un simile valore stringa sta nel fatto di ottenere una stringa priva di ogni contenuto. Per imporre che s sia una stringa nulla si assegna: s = “”; Attenzione di non confondere questo valore (“”) con quello null precedentemente considerato. Il valore null è tipico di alcuni tipi di variabili, per indicare che non vi è stata ancora alcuna assegnazione oppure nessuna istanza riguardante la variabile. La lunghezza di una stringa nulla è pari a 0. La stringa nulla viene usata come valore di “azzeramento” per le variabili string. Operatore di concatenazione (+) L’operatore più usato ed importante sulle stringhe (e già utilizzato sopra) è quello detto di concatenazione, presente infatti in molti linguaggi oltre a C#. In C# esso è definito dal simbolo +. In realtà questo simbolo non indica in questo caso somma, ma semmai accostamento e quindi “somma” in senso lato di stringhe, che vanno a formare un'unica stringa più grande. E’ possibile quindi avere espressioni del tipo: s = “pippo” + “ e “ + “pluto”; // concatenazione di (tre) costanti oppure s = s2 + “pippo”; // concatenazione di un valore costante e di una var. stringa oppure s = s1 + s2; // concatenazione di due variabili stringa Come ovvio la stringa risultante dalla concatenazione viene assegnata nel nostro caso ad s. Assegnazione dovuta a lettura su console Una stringa può essere anche valorizzata tramite una lettura da console. In questo caso si utilizza ancora il famoso comando Console.ReadLine(), che in realtà rende una una riga di testo, ossia una stringa (la stringa digitata sulla console, fino alla pressione del tasto Invio). In questo caso, data la natura del comando, non è necessaria alcuna conversione del dato che può essere direttamente utilizzato: s = Console.ReadLine(); // lettura da console di una stringa Con la stessa istruzione sarà possibile avere i casi particolari di un input di un solo carattere (stringa di lunghezza 1), e di input di nessun carattere (stringa di lunghezza 0), in cui quindi s contiene una stringa nulla (“”). Lunghezza di una stringa Una delle caratteristiche che più spesso vengono richieste ad una stringa è conoscere la sua lunghezza. Questo dato è una delle proprietà dell’oggetto string considerato. Qualunque variabile stringa, quindi, come oggetto ha una proprietà che rende la sua lunghezza, detta Length: l = s.Length; dove l è una variabile int e s una var. string. Si noti che le proprietà vanno scritte senza parentesi, in quanto esse rappresentano direttamente un valore associato all’oggetto (in questo caso la lunghezza della stringa s). Istruzioni ToUpper e ToLower Data una stringa è possibile facilmente rendere tutti i suoi contenuti maiuscoli o minuscoli. Ovviamente ciò per i comuni caratteri alfabetici (A-Z). Tutti gli altri simboli e caratteri rimangono invariati e tali comandi non hanno effetto su loro. Pagina 2
Ad esempio: s = “Pippo”; s = s.ToUpper(); Console.WriteLine(s); rende la stampa PIPPO tutta in maiuscolo. Il metodo ToLower(), invece converte tutte i caratteri (A-Z) in minuscolo. switch con valori string L’istruzione switch in C# può operare anche con valori di tipo stringa. Ciò può dare una maggior flessibilità alla codifica in alcuni casi. Ad esempio quindi è possibile effettuare una selezione con casi multipli in base al contenuto di una variabile stringa: switch(s) { case “abc”: ….. break; case “def”: ….. break; …. default: ….. break; } La selezione del caso sarà data dall’uguaglianza del valore di s con una delle stringhe riportate come riferimento (nel nostro caso “abc” e “def”, ecc.). Confronto fra stringhe Sebbene potremmo non rendercene conto, operando in informatica, l’insieme delle stringhe è un insieme ordinato. Ciò vuol dire che è possibile confrontare stringhe e definire un ordine fra loro in base a certi criteri. C# esegue confronti tra stringhe (e quindi tra contenuti di variabili string) in base a tali criteri, in modo del tutto automatico. Gli operatori di confronto applicabili a stringhe sono quelli usuali validi anche per valori numerici: == uguaglianza != diversità Per i confronti riguardanti le operazioni di ordinamento di stringhe vere e proprie, C# non usa i tradizionali operatori >, < , >=,
Quindi per l’utilizzo in condizioni, bisognerà aggiungere, oltre all’istruzione sopra, operatori di confronto fra interi: if (s1.CompareTo(s2) > 0) ad esempio, la condizione qui sopra, se vera, comporta che s1 sia maggiore di s2. Criteri del confronto Per il confronto tra stringhe, per le stringhe che siano composte solo da caratteri alfabetici tradizionali (a-z, A-Z), valgono in realtà applicate le regole di ordinamento alfabetico classico: Quindi ad esempio: “albero” == “albero” risulta vero mentre “albero” > “casa” risulta falso In quanto albero è alfabeticamente inferiore a casa (in un comune dizionario la parola “casa” è nelle pagine successive ad “albero”, quindi ha un “valore più alto”). In modo analogo vanno intesi ed applicati tutti gli altri confronti. Ci sono però due complicazioni a questa semplice e chiara regola di ordinamento: Il computer non considera (a ragione) uguali i caratteri alfabetici maiuscoli e minuscoli Le stringhe devono essere tutte confrontabili da qualunque serie di caratteri esse siano costituite (quindi non solo quelle scritte in caratteri alfabetici (a-z, A-Z)) In realtà come detto più volte all’inizio dell’anno, il computer trasforma qualunque dato in numero e tratta come tale lo stesso. Ciò vuol dire che anche i singoli caratteri che compongono una stringa sono codificati in forma numerica. Ciò spiega meglio perché ‘a’ non sia uguale ad ‘A’, e così via per gli altri caratteri maiuscoli / minuscoli. ‘A’ ed ‘a’ hanno in effetti due codici ASCII distinti (si veda tabella ASCII a seguire). Si noti anche che considerata la tabella ASCII (valida anche nel linguaggio C#), mantiene l’ordinamento alfabetico sia sulle lettere maiuscole, che sulle minuscole, in ambiti separati. In sostanza i codici delle lettere alfabetiche (A-Z) maiuscole sono progressivi a salire da 65 corrispondente ad ‘A’ a codici subito seguenti corrispondenti a ‘B’, ‘C’ e così via. Analoga cosa per le minuscole con ‘a’ corrispondente al codice 97 e ‘b’, ‘c’ ecc. con codici contigui a seguire. In realtà alfabeticamente ‘B’ > ‘A’ e così è anche sotto forma di codice. Una considerazione del tutto simile vale anche sulle lettere minuscole, confrontabili tramite i codici ASCII. Considerata la codifica ASCII infatti tutte le minuscole sono “maggiori” delle corrispondenti maiuscole, ed anzi di tutte le maiuscole (si osservi con attenzione la tabella per convincersene). Ciò comporta una serie di disuguaglianze fra stringhe non del tutto logica secondo l’ordinamento alfabetico tradizionale (che non distingue tra maiuscole e minuscole): “Albero” < “albero” oppure “CASA” < “casa” oppure “casA” < “casa” oppure “ZAPPA” < “albero” Queste disuguaglianze danno tutte il valore vero (ossia sono tutte vere). Per la prima disuguaglianza si può considerare solo la prima lettera, che poi è l’unica diversa nelle due stringhe. In realtà, per procedere al confronto, come nell’ordinamento alfabetico classico, si considera lettera per lettera, da sinistra a destra, individuando un ordine sul primo carattere, e se risulta uguale, si procede sul secondo, e se uguale, sul terzo e così via. E quindi già sul primo carattere di “Albero” confrontato con “albero”, la ‘A’ ha codice 65, mentre ‘a’ ha codice 97, quindi il calcolatore considerando i codici dei caratteri conclude che “Albero” sia già inferiore ad “albero”. La stessa cosa accade con “CASA” e “casa”, già alla prima lettera ‘C’ (67 ASCII) e ‘c’ (99 ASCII). Pagina 4
Se, come nell’esempio successivo, la parola fosse coincidente, e la differenza maiuscolo / minuscolo di una lettera coinvolgesse una lettera seguente (qui l’ultima), si dovrebbe procedere confrontando fino ad essa (qui fino all’ultimo carattere), e concludere che la parola “casA” è inferiore a “casa”, sempre per il fatto che ‘A’ < ‘a’. Ovviamente vi sono dei casi ancora meno chiari per un ordinamento basato su criteri usuali o “umani”: Ad esempio ci si può chiedere se “$?/+” < “#@*-“ A questo problema non è possibile rispondere se non con i criteri di ordinamento prospettati prima, ossia basati esclusivamente sui codici ASCII. Riepiloghiamo quindi i passi della metodica di confronto fra stringhe: 1. Si parte con il confrontare il primo carattere delle due stringhe 2. Il criterio del confronto si basa esclusivamente sul codice ASCII del carattere stesso. Il carattere con codice ASCII maggiore è considerato superiore a quello con codice ASCII inferiore. 3. In base a questo è possibile confrontare non solo caratteri dell’alfabeto (a-z, A-Z), ma qualunque carattere o simbolo 4. Se il primo carattere è diverso => una delle stringhe viene considerata superiore all’altra (quella avente codice ASCII più alto sul primo carattere) e il confronto termina. 5. Se il primo carattere coincide, itero il confronto sui caratteri successivi corrispondenti (secondo col secondo, terzo col terzo…), ad uno ad uno, finchè non individuo caratteri diversi in base ai quali possa individuare la stringa maggiore o minore (vd. punto 4) 6. Se le due stringhe hanno lunghezza diversa e se tutti i caratteri fino alla fine della stringa più corta sono uguali, risulta maggiore quella avente ulteriori altri caratteri (ossia la più lunga) 7. Se tutti i caratteri sono uguali e le stringa hanno stessa lunghezza concludo che sono effettivamente uguali. Pagina 5
Caratteri e stringhe di lunghezza 1 Sebbene ci possa essere una iniziale confusione in realtà è bene convincersi che il carattere ‘A’ è diverso operativamente dalla stringa contenente “A”. Questo se ci si fa attenzione è già evidenziato dalla differente delimitazione del dato alfanumerico: Apice singolo nel caso dei char (un solo carattere) Apici doppi nel caso di un valore string (anche se è lungo un solo carattere) In realtà C#, giustamente, non permette il passaggio di dati da un tipo all’altro in modo implicito o sottointeso. Stringhe come vettori Sebbene formalmente le stringhe siano diverse dai vettori di char, esse sono assimilabili in certi momenti ad essi. Infatti data una stringa è possibile accedere ai singoli caratteri della stessa in sola lettura, e leggere carattere per carattere la stringa stessa: s =”Natale”; il codice C#: for (i = 0; i < s.Length; i++) Console.Write(s[i]); stamperà anch’esso la scritta “Natale”, ma leggendo carattere per carattere il contenuto di s. Si tenga presente che le regole dell’indice per l’accesso ai singoli caratteri di una stringa seguono le regole già definite per i vettori, vale a dire: L’indice corrispondente al primo carattere della stringa è 0 Per i caratteri intermedi gli indici sono contigui L’indice dell’ultimo carattere, dipende dalla lunghezza della stringa stessa e sarà s.Length - 1 In realtà una prima differenza tra stringhe e vettori di char si nota dal fatto che è possibile leggere i singoli caratteri, ma non è possibile modificarli. Ad esempio l’operazione: s[0] = ‘n’; non è consentita in C#. Bisogna anche ricordare ed avere presente che quando si accede ai singoli caratteri di una stringa, con la tecnica sopra descritta, il tipo di dato che viene estratto è di tipo char, ossia eventuali confronti devono essere fatti con costanti o variabili di tipo carattere (char) e non string. Ad esempio se devo controllare che il carattere 2 della precedente stringa non sia ‘X’: if ((s[2] != 'x') && (s[2] != 'X')) Console.WriteLine("Il carattere di indice 2 non è x o X"); Conversioni da char a string e da string a char Consideriamo il problema di convertire un singolo carattere in una stringa. Se: string s; char ch = ‘A’; In realtà non è possibile l’assegnazione diretta, in quanto ch è di tipo differente da string: s = ch; // è errato Un modo possibile per inserire ch in s è: s = “” + ch; ossia fare si che ch appaia come elemento concatenato ad una stringa nulla1. Un altro modo per ottenere ciò è una conversione esplicita del carattere a stringa con il comando (metodo) ToString(): s = ch.ToString(); 1 Da ciò si deduce che l’operatore + opera anche su dati di tipo char (convertendoli automaticamente in string). Pagina 6
In tal caso s conterrà alla fine il singolo carattere ch, ma ovviamente sarà vista come una stringa. Viceversa per far diventare una stringa di un solo carattere un char, basterà scrivere l’operazione: ch = s[0]; che permette di passare il primo (ed ipotizzato unico) carattere della stringa in ch. Si consideri anche che anche i tipi di dato int, float, double, bool possono essere convertiti al loro equivalente alfanumerico usando il metodo ToString(). Ad esempio: double d; … d = 134.59; s = d.ToString(); ora s contiene la stringa “134.59”2. Si noti anche che questo tipo di operazioni sono il simmetrico di quelle effettuate con il comando (metodo) Parse(…), su varie stringhe, in cui da stringa si portava il dato verso int, double, bool ecc. 2Su sistemi Windows italiani la conversione sarà effettuata come “134,59” in quanto il punto di separazione tra interi e cifre decimali, viene convertito, coerentemente alla lingua corrente, in virgola. Pagina 7
Puoi anche leggere