#13 – Manipolare le Stringhe su Arduino

Prima di imparare a comunicare con la scheda Arduino, dobbiamo conoscere la manipolazione di stringhe di testo.

Utilizzare le stringhe di testo in un linguaggio di programmazione a basso livello, come C, è sempre impegnativo. Ci sono però diversi modi per utilizzare le stringhe su Arduino.

Oltre a poter trattare le stringhe come nello standard C, grazie all’influsso di C++ troviamo in Arduino anche un oggetto chiamato “String”, che vuole semplificare la questione.

Stringhe come array di caratteri

Vedremo inizialmente il modo nativo di lavorare con le stringhe, dato che è quello che garantisce migliori prestazioni e un maggiore controllo.

Stringa come array di caratteri, chiuso dal terminatore

Definiamo stringa un array di caratteri, in cui l’ultimo carattere è chiamato terminatore.

I caratteri sono salvati in ASCII, una particolare codifica che utilizza un byte di spazio (ma in realtà sono usati solo 7 bit). Nella pratica, ogni carattere è associato e trattato come fosse un numero intero senza segno, da 0 a 127:

Valori ASCII

Il carattere terminatore ha codice ASCII 0.

Esistono diversi modi per creare ed inizializzare una stringa in memoria. Il metodo classico è lo stesso visto per gli array:

char stringa[7] = {'a', 'n', 'd', 'r', 'e', 'a', '\0'};

Vediamo subito dall’esempio come scrivere il carattere terminatore. È fondamentale riservare sempre un carattere in più per il terminatore, altrimenti tutte le funzioni che operano sulle stringhe non funzionano.

Poiché la precedente inizializzazione è macchinosa quando vogliamo salvare stringhe già note, possiamo utilizzare la seguente:

char stringa[] = "andrea";

Utilizziamo ” ” quando scriviamo una sequenza di caratteri, mentre ‘ ‘ per indicare un singolo carattere. Come possiamo osservare, non abbiamo specificato la dimensione, né aggiunto il carattere terminatore. Sarà il compilatore a ricreare in automatico la prima inizializzazione vista, nel momento in cui invieremo il codice alla scheda. Sarà sempre il compilatore ad aggiungere il carattere terminatore alla sequenza.

Se utilizziamo le stringhe in questo modo, possiamo appoggiarci alle funzioni standard del C:

  • strlen(str): restituisce il numero di caratteri presenti prima del carattere terminatore.
  • strcpy(dest, source): copia la stringa source all’inizio della stringa dest (eventualmente risovrascrivendo dest)
  • strcat(dest, source): aggiunge la stringa source alla fine della stringa dest.
  • strcmp(str1, str2): confronta str1 e str2, e restituisce 0 se sono uguali, altrimenti un numero <0 se str1 viene prima in ordine lessicografico di str2, altrimenti >0 se str1 viene dopo.

Quando utilizziamo comandi come strcpy e strcat, dobbiamo prestare attenzione che il buffer di destinazione sia abbastanza grande, altrimenti genereremo errori a runtime.

void setup() {
  bool risultato;
 
  char str1[] = "andrea";
  char str2[] = "aspesi";
  char str3[20];
  
  //Copio str1 in str3
  strcpy(str3, str1);
  
  //Appendo str2 in str3
  strcat(str3, str2);
  
  //Verifico se il risultato è uguale ad andreaaspesi
  if (strcmp(str3, "andreaaspesi") == 0){
    risultato = 1;
  }else{
    risultato = 0;
  }
  
  //Ovviamente risultato è uguale a 1
}

void loop() {
  
}

Le funzioni che operano sulle stringhe, si occupano in automatico dei terminatori.

Per scorrere i caratteri di una stringa di lunghezza incognita, utilizziamo:

for (int i=0; str[i] != '\0'; i++){
  //str[i] contiene il carattere alla posizione i-esima
}

Oggetto String

Poiché il linguaggio di programmazione di Arduino non è puro C, ma presenta anche caratteristiche di C++ (linguaggio di programmazione ad oggetti), troviamo quelli che vengono chiamati oggetti.

Gli oggetti sono speciali “contenitori”, che hanno al loro interno variabili e funzioni. Alcune accessibili dall’esterno del contenitore, altre no.

Le variabili/funzioni accessibili dall’esterno vengono chiamate pubbliche, mentre le altre private.

Possiamo vedere gli oggetti come dei “modelli”. Quello che li differenzia da altre componenti del codice, come ad esempio le librerie di funzioni, è il fatto che gli oggetti possono essere “istanziati”.

Quando creiamo una istanza di un oggetto, ne creiamo una copia in memoria, basata sul modello, ma che può essere riempita con valori specifici. Ad esempio, se creiamo una istanza di un oggetto “String”, possiamo assegnare all’istanza una particolare stringa di testo da tenere in memoria.

L’oggetto è quindi il modello base, mentre l’istanza è la sua contestualizzazione attraverso i dati.

Nel caso dell’oggetto String, troviamo le seguenti funzioni esposte (tra le tante):

Per istanziare l’oggetto, utilizziamo:

//Se utilizziamo una costante
String stringOne = "Ciao";     
String stringOne = String('a');
String stringOne = String("Questa e' una stringa");
String stringOne = String(13); //Con un numero intero
String stringOne = String(analogRead(0), DEC); //Con un numero intero e base 10
String stringOne = String(45, HEX); //Con un numero intero, e base esadecimale
String stringOne = String(255, BIN); //Con un numero intero, e in binario
String stringOne = String(5.698, 3); //Con un float, e 3 cifre decimali

//Concatenazione
String stringOne = String(stringOne + " with more");  

Riscriviamo il codice visto in precedenza con il nuovo oggetto:

void setup() {
  bool risultato;
 
  String str1 = "andrea";
  String str2 = "aspesi";
  String str3;
  
  //Copio str1 in str3
  str3 = str1;
  
  //Appendo str2 in str3
  str3.concat(str2);
  
  //Verifico se il risultato è uguale ad andreaaspesi
  if (str3.compareTo("andreaaspesi") == 0){
    risultato = 1;
  }else{
    risultato = 0;
  }
  
  //Ovviamente risultato è uguale a 1
}

void loop() {
  
}

In conclusione

Sebbene sia più semplice utilizzare l’oggetto String, è possibile utilizzare meno memoria con le stringhe viste come array di caratteri (come nello standard C).

Il consiglio è quello di utilizzare l’oggetto String solo se nel vostro codice manipolate spesso le stringhe.

Ora che abbiamo visto come manipolare le stringhe su Arduino, andiamo a vedere il vero motivo per cui lo facciamo, nel prossimo articolo.