Home > Blockchain >  Nested strtok in Arduinoproject
Nested strtok in Arduinoproject

Time:12-09

I'm coding a parser for my Arduinoproject. I have a long charArray that contains multiple "packages" in the form of:

#           package beginns
adress
;           seperating character
adress
;           seperating character
command
;           seperating character
data
*           package ends

One input could be:

#A0001;B0001;A;2456;*#A0002;B0002;B;7615;*#A0003;B0003;C;8943;*

The goal is to write a Parser that takes a charArray and splits it at one char ('#') and than a second time with another char (';'). The problem is that the outer splitting is only done once and than stopps. I think it has something to do with the pointer, but don't know what to do.

My Code:

void setup() {
  
  Serial.begin(9600);
  char test[] = "#A0001;B0001;A;2456;*#A0002;B0002;B;7615;*#A0003;B0003;C;8943;*";
  Serial.print("Test: ");
  Serial.println(test);
  Serial.println("--------------------");
  
  parseCurrentStream(test);
  Serial.println("-----------END_OF_CODE------------");
}

void parseCurrentStream(char* input){
  int commandCount = getCountOfCharInString(input, '*');
  package packageList[commandCount];
  commandCount = 0;
  
  char* piece = strtok(input, "#");
  while (piece != NULL){
    Serial.println(piece);
    
    package pack = getPackage(piece);
    printPackage(pack);

    commandCount  ;
    piece = strtok(NULL, "#");
  }
}

package getPackage(char* packageString){
  Serial.println("---Start_Parsing---");
  Serial.print("StringInput: ");
  Serial.println(packageString);
  
  bool corruptedData = false;
  
  if(!isEndCharExisting(packageString)){
    corruptedData = true;
  }
  
  // final Fields
  char* receiver;
  char* transmitter;
  char command;
  char* data;
  bool empty;
  
  char* piece = strtok(packageString, ";");
  int counter; 
  
  while (piece != NULL && !corruptedData){
    Serial.print(corruptedData);
    Serial.print(" | Piece ");
    Serial.print(counter);
    Serial.print(": ");
    Serial.println(piece);  
    switch(counter){
      case 0:
        if(strlen(piece) == 5){
          receiver = piece;
        } else {
          corruptedData = true;      
        }
      break;
      case 1:
        if(strlen(piece) == 5){
          transmitter = piece;
        } else {
          corruptedData = true;      
        }       
      break;
      case 2:
        if(strlen(piece) == 1){
          command = piece[0];
        } else {
          corruptedData = true;      
        }
      break;
      case 3:
        if(strlen(piece) == sizeOfCommand(command)){
          data = piece;
        } else {
          corruptedData = true;      
        }
      break;
      case 4:
        if(!(strlen(piece) == 1 && piece[0] == '*')){
          corruptedData = true;
        }
      break;
      default:
        corruptedData = true;
      break;
    }
  
    counter  ;
    piece = strtok(NULL, ";");
  }

  struct package finalPackage;
  if(corruptedData){
    finalPackage = {receiver, transmitter, command, data, true};
  } else {
    finalPackage = {receiver, transmitter, command, data, false};
  }
  Serial.println("---End_Parsing---");  
  return finalPackage;
}

Output:

Test: #A0001;B0001;A;2456;*#A0002;B0002;B;7615;*#A0003;B0003;C;8943;*

--------------------
A0001;B0001;A;2456;*
---Start_Parsing---
StringInput: A0001;B0001;A;2456;*
0 | Piece 0: A0001
0 | Piece 1: B0001
0 | Piece 2: A
0 | Piece 3: 2456
0 | Piece 4: *
---End_Parsing---
Package: 
A0001
B0001
A
2456
0
-----------------
-----------END_OF_CODE------------

You can see that for the first iteraton it works perfectly, but the second "string" (#A0002;B0002;B;7615;*) is not parsed...

When i'm not using my function getPackage() the substrings are printed as expected. I have found some advice to copy the char before inserting it to the function but this also didn't help.

char pieceCopy[strlen(piece)];
strcpy(pieceCopy, piece);

CodePudding user response:

Because you are splitting for different tokens at the same time (both in parseCurrentStream and in getPackage), you are messing up the internal state of strtok. However, using one strtok_r for each tokenization, you can provide a pointer to a char pointer, that each different strtok_r can use to save its current state. See the example in the link provided. strtok_r is especially useful in multi-threaded environments.

char *strtok_r(char *str, const char *delim, char **saveptr);

char str[] = "the quick brown fox";
char* token;
char* rest = str;

while ((token = strtok_r(rest, " ", &rest)))
    printf("%s\n", token);

CodePudding user response:

Thanks for all of your support!!! For me the solution of @Gerhardh worked perfectly.

The strsep() was availbale for me.

My code looks like this now:

void parseCurrentStream(char* input){
  int commandCount = getCountOfCharInString(input, '*');

  char* piece;
  char* string;
  string = strdup(input);

  while( (piece = strsep(&string,"#")) != NULL ){
      if(strlen(piece) > 0){
        package pack = getPackage(piece);
        printPackage(pack);
      }    
  }
}

package getPackage(char* packageString){
  Serial.println("---Start_Parsing---");
  Serial.print("StringInput: ");
  Serial.println(packageString);
  
  bool corruptedData = false;
  
  // final Fields
  char* receiver;
  char* transmitter;
  char command;
  char* data;
  bool empty;
  
  //char* piece = strtok(packageString, ";");
  int counter = 0; 

  char* piece;
  char* string;
  string = strdup(packageString);
  
  while ((piece = strsep(&string,";")) != NULL){
    Serial.print(corruptedData);
    Serial.print(" | Piece ");
    Serial.print(counter);
    Serial.print(": ");
    Serial.println(piece);  
    switch(counter){
      case 0:
        if(strlen(piece) == 5){
          receiver = piece;
        } else {
          corruptedData = true;      
        }
      break;
      case 1:
        if(strlen(piece) == 5){
          transmitter = piece;
        } else {
          corruptedData = true;      
        }       
      break;
      case 2:
        if(strlen(piece) == 1){
          command = piece[0];
        } else {
          corruptedData = true;      
        }
      break;
      case 3:
        if(strlen(piece) == sizeOfCommand(command)){
          data = piece;
        } else {
          corruptedData = true;      
        }
      break;
      case 4:
        if(!(strlen(piece) == 1 && piece[0] == '*')){
          corruptedData = true;
        }
      break;
      default:
        corruptedData = true;
      break;
    }
  
    counter  ;
  }
  • Related