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 ;
}