Tip iOS #36 – Suddividere in token una stringa

iOSBuongiorno a tutti,

lavorare con le stringhe è sicuramente uno dei compiti più frequenti che capita di dover affrontare quando si sviluppa un’applicazione iOS.

Oltre ai compiti soliti come il controllo sulla lunghezza, il concatenamento e la formattazione, quello che sicuramente capita più frequentemente di affrontare, soprattutto quando si ricevono dati dall’esterno, è la suddivisione di una stringa in sottoelementi, denominati token, in base a determinate regole.

Questo compito è frequentemente associato al trattamento di dati come i files CSV (Comma Separated Values) dove ogni riga rappresenta un record ed i campi sono espressi in sequenze ordinate separate solitamente da un marcatore (la virgola o il punto e virgola, ma potrebbero essere altri).

Ma sono diversi i casi in cui si potrebbe rendere necessaria la suddivisione in token di una stringa, di metodi ne esistono differenti, alcuni di più semplice implementazione (ma che richiedono un maggior uso di memoria ed una minore flessibilità), altri più complessi, relativamente flessibili ma richiedono meno memoria, altri estremamente flessibili ma altrettanto complessi da implementare.

La scelta varia di volta in volta in base alle proprie esigenze, va ricordato, però, che si parla sempre di dispositivi mobile, anche se estremamente potenti,  e quindi è preferibile adottare la soluzione che impiega la minore memoria possibile.

Nel nostro esempio proveremo a suddividere in token la stringa “COME STAI ? SPERO BENE” usando come separatore lo spazio

1. Adoperando il metodo componentsSeparatedByString della classe NSString

NSString *testo = @"COME STAI ? SPERO BENE";
NSArray *elementi = [testo componentsSeparatedByString:@" "];

La soluzione proposta non fa altro che prendere una stringa e suddividerla in un array di stringhe in base al separatore indicato, che viene omesso nell’array di destinazione. Se si ha a che fare con stringhe di cui si è certi della natura e della suddivisione, esso è sicuramente un metodo rapido di implementare il procedimento. Soffre però di due ostacoli rilevanti. Il primo e più evidente lo si ha con l’occupazione di memoria, infatti se la stringa di partenza è abbastanza grossa, al termine dell’operazione di suddivisione si avrà una quantità di memoria occupata pari almeno a quella della stringa di partenza più l’array di destinazione. Anche se si può pensare che tale quantità sia esigua rispetto alla ram complessiva, va ricordato che grazie al multitasking ed al sistema opeativo, in realtà la memoria libera potrebbe essere anche molto bassa. Il secondo ostacolo è rappresentato dalla flessibilità, l’operazione è, infatti, eseguita in modalità one-shot senza poter intervenire in step intermedi, se la sintassi della stringa di partenza risultasse più complessa la complessità del codice crescerebbe così come la quantità di memoria occupata, sempre che la soluzione sia percorribile.

2. Adoperando la classe NSScanner

NSString *testo = @"COME STAI ? SPERO BENE";
NSScanner *scanner = [NSScanner scannerWithString:testo];
NSString *token;
while ([scanner scanUpToString:@" " intoString:&token]) {
    // Elabora il token
}

Questa seconda soluzione propone un codice lievemente più complesso, ma non troppo, a fronte di un consumo di memoria notevolmente ridotto e ad un incremento della flessibilità. Infatti la memoria occupata è quella della stringa di partenza insieme alla memoria occupata dalla classe e quella del singolo token. Questo permette di gestire stringhe notevolmente più lunghe riducendo il rischio di esaurimento della memoria RAM. Il secondo vantaggio è che l’estrazione dei token è interattiva, difatti ogni qualvolta un nuovo token viene estratto esso può essere immediatamente “lavorato” permettendo quindi aggiustamenti, interruzioni di flusso, o elaborazioni estemporanee, comprese sottoelaborazioni per token maggiormente complessi. Lo svantaggio sta, anche qui, nella gestione di stringhe con regole molto complesse dove la tokenizzazione potrebbe richiedere la scrittura di codice aggiuntivo.

3. Adoperando la classe NSRegularExpression

NSString *testo = @"COME STAI ? SPERO BENE";
NSError *error = NULL;
NSRegularExpression* regex = [NSRegularExpression regularExpressionWithPattern:@"\\w+" options:0 error:&error];
if (!regex && error) {
    // Segnalare l'errore
} else {
    NSArray *trovati = [regex matechesInString:testo options:0 range:NSMakeRange(0, [string length])];
    for(NSTextCheckingResult *match in trovati) {
        NSRange range = [match range];
        // Una volta ottenuto il range possiamo estrarre il token ed elaborarlo
    }
}

Questo metodo è sicuramente il più complesso dei tre ma, al contempo, anche il più flessibile. Difatti tramite esso, conoscendo le regole sintattiche della stringa di partenza, possiamo suddividerla anche in token adoperando regole complesse. Inoltre l’approccio è simile a quello dell’NSScanner e quindi la quantità di memoria adoperata è comunque limitata (anche se maggiore in quanto non otteniamo il token ma un array di range in cui è presente il separatore). Un altro indiscusso vantaggio di questo approccio è che la classe NSRegularExpression è thread safe e può essere adoperata nei blocchi di codice. Poiché l’elaborazione delle stringhe è un compito particolarmente gravoso poterla eseguire su un thread dedicato permette di poter far lavorare l’applicazione in modo più efficiente. Unica limitazione la stringa da elaborare non deve mutare per tutto il tempo in cui è sottoposta a lavorazione. La vera difficoltà di questo approccio, oltre al codice più complesso, è data dalla conoscenza necessaria delle regular expressions per poter sfruttare al meglio l’elaborazione.

Arrivederci al prossimo suggerimento,

Roberto S.

 



Related Posts Plugin for WordPress, Blogger...

Questa voce è stata pubblicata in iOS Tips e contrassegnata con , , , , , , , , . Contrassegna il permalink.
Wordpress Code Snippet by Allan Collins