Split stringa in Arduino

Split di una stringa in Arduino

Quando si implementa la comunicazione seriale tra Arduino ed un altro strumento, potrebbe essere molto utile effettuare lo split di una stringa ricevuta in base ad un carattere separatore. Ma non solo. Potreste avere anche bisogno di effettuare lo split su una stringa letta da un file, ad esempio.

Nel caso infatti in cui il messaggio, o la stringa in generale, contenga più informazioni, sarà necessario inserire un carattere separatore per indicare quando iniziano e finiscono i vari campi del messaggio e quindi separarli.

Molti linguaggi di programmazione implementano già funzioni che effettuano lo split di una stringa in base ad un separatore. La classe String di Arduino però non prevede questa funzionalità.

Vediamo quindi due esempi su come poter implementare la funzione split. Nel primo esempio effettueremo lo split su una variabile di tipo String, nel secondo caso invece su un array di char.

Split su variabile String

In questo esempio partiremo da una variabile String fissa e vedremo come fare ad effettuare lo split. Ovviamente quello che vedremo potrà essere applicato nel caso in cui la stringa si stata ricevuta sulla seriale, letta da un file etc…

Definiamo una variabile String.

String myString = "1111:2222:3333";

In questo caso abbiamo scelto “:” come carattere separatore. I campi da leggere sono dunque 3. Nel caso in cui la stringa venga inviata tramite messaggio sulla seriale da un altro software, sarà necessario conoscere il carattere separatore usato dal sw e anche il numero di “campi” del messaggio presenti o che ci interessa leggere.

Nella funzione setup() inizializziamo la seriale e chiamiamo la funzione che andremo a creare per effettuare lo split.

void setup() {
 // put your setup code here, to run once:
 Serial.begin(9600);
 
 String value1 = splitString(myString, ':', 0);
 String value2 = splitString(myString, ':', 1);
 String value3 = splitString(myString, ':', 2);
 
 Serial.println("Y:" + value1);
 Serial.println("X:" + value2);
 Serial.println("Z:" + value3);
}

La funzione per effettuare lo split si chiamerà splitString (potete chiamarla come preferiete). Prendendo ad esempio la riga

String value1 = splitString(myString, ‘:’, 0);

vediamo che la funzione ha in ingresso tre valori

  • myString – la stringa da “splittare”
  • ‘:’  – il carattere separatore
  • 0 – l’indice del campo del messaggio che si vuole leggere

La funzione in questo caso dovrà quindi restituire il valore “1111”.

Vediamo dunque come implementare la funzione splitString.

String splitString(String str, char sep, int index)
{
 int found = 0;
 int strIdx[] = { 0, -1 };
 int maxIdx = str.length() - 1;

 for (int i = 0; i <= maxIdx && found <= index; i++)
 {
    if (str.charAt(i) == sep || i == maxIdx)
    {
      found++;
      strIdx[0] = strIdx[1] + 1;
      strIdx[1] = (i == maxIdx) ? i+1 : i;
    }
 }
 return found > index ? str.substring(strIdx[0], strIdx[1]) : "";
}

Spieghiamo brevemente quello che avviene nella funzione.

In base al valore dell’input index decideremo quando fermarsi nella lettura della stringa. Infatti, come potete vedere, il ciclo for verrà interrotto quando found, incrementato ogni volta che si incontra il carattere separatore, sarà maggiore di index (o quando si sono terminati i caratteri della stringa).

Quando viene trovato il carattere separatore vengono impostato i due valori delimitatori strIdx[0] e strIdx[1].

Ad esempio, supponendo che index = 0, il primo carattere separatore verrà individuato quando i = 4. A questo punto avremo

strIdx[0] = -1 + 1 = 0  (il -1 deriva dal fatto che strIdx[1] è inizializzato a -1)

strIdx[1] = 4

Nel caso index = 0, il ciclo for verrà interrotto appena si trova il primo “:”. Quindi verrà restituito il risultato di

str.substring(strIdx[0], strIdx[1]);

ovvero la sottostringa compresa tra 0 e 4 che è proprio il valore “1111”.

Quando si vuole leggere invece il secondo campo avremo index = 1, dunque il ciclo for non si fermerà al primo carattere separatore trovato, ma al secondo che si trova per i = 9 e quindi avremo.

strIdx[0] = 4 + 1 = 5  (il 4 deriva dal fatto che strIdx[1] viene impostato = 4 quando si trova il primo “:” durante il ciclo)

strIdx[1] = 9

La funzione substring restituirà quindi la stringa compresa tra 5 e 9 che è appunto “2222”.

Testate il codice qui riportato con il vostro Arduino e quello che dovete ottenere sul monitor seriale sarà

V1: 1111

V2: 2222

V3: 3333

Split su array di char

Supponiamo che invece di una variabile String, venga letto un messaggio dalla seriale e memorizzato in un array di char. (Anche in questo caso, nell’esempio, partiremo da un array di char fisso senza riceverlo da un altro software. Quello che ci interessa è mostrarvi come splittare l’array, non come ricevere dati dalla seriale).

Supponiamo di avere l’array

char stringa[] = "1111;2222;3333";

In questo caso il carattere separatore è “;”.

La funzione setup() sarà nel seguente modo

void setup()
{
 char *p = stringa;
 char *str;
 int index = 0;
 String values[3];

 Serial.begin(9600);
 while ((str = strtok_r(p, ";", &p)) != NULL) 
 {
    values[index] = str;
    index++;
 }

 for(int i = 0; i < 3; i++)
    Serial.println(values[i]);
}

void loop()
{}

Questo esempio fa uso della funzione strtok_r. A questo link potete trovare una spiegazione delle funzioni strtok e strok_r . Si tratta di due funzioni presenti nelle librerie del linguaggio C, che effettuano il parsing di una stringa dividendola in più tokens, ovvero blocchi di testo, in base al carattere separatore in input.

Nel nostro esempio abbiamo memorizzato poi gli array di char letti dalla funzione strtok_r nell’array String values[3], per poterli poi stampare a video ed ottenere come risultato sul monitor seriale i valori

1111

2222

3333