I'm writing code in which several command strings are represented as char arrays.
A typical command string looks like [1p=123.456].
Each command string starts with one of the following character sequences:
[1
[2
{2
{
I want to strip these leading chars from the char array, so it's easier to use sscanf to extract the numeric value.
What's the most efficient and robust way of doing this with standard C functions? I could use a loop to copy all but the first one or two chars to a new char array, but that seems a little clunky?
westfw
September 27, 2019, 9:41am
2
What's the most efficient and robust way of removing characters with standard C functions?
Assuming that you have something like:
char mycmd[] = "[1p=123.456]";
Just use a pointer incremented beyond the first character:
char *p = &mycmd[2]; // skip the first two characters
sscanf() itself has some pretty strong pattern matching capabilities. I don't know how efficient they are.
1 Like
Thank you - I'll try your pointer idea which I guess is as efficient as it can get!
I've tried
sscanf(mycmd,"%*[[1]%s",&cmd);
which works, but I suspect with way more overhead than your solution.
I've just tried the pointer suggestion and the ESP8266 resets itself - I must have something wrong...
Here's the code:
void processCmd(char *rx) {
char *cmd;
if (rx[0] == '[') {
cmd = &rx[2];
printf("cmd=%s\n",cmd);
}
}
void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) {
char rxChars[128];
switch(type) {
case WStype_TEXT:
processCmd((char *)payload);
break;
}
}
Where have I gone wrong with the pointer/address syntax?
gfvalvo
September 27, 2019, 11:02am
5
You're assigning the pointer correctly and then doing nothing with it.
I've added a printf to the example code - is this what you mean?
No, here
void processCmd(char *rx) {
char *cmd;
if (rx[0] == '[') {
cmd = &rx[2];
printf("cmd=%s\n",cmd);
}
}
Please excuse my ignorance - I don't understand what you mean.
You've got a local variable called cmd, but you don't do anything useful with it.
In reality, there's a whole load of code in place of printf. I removed it so I could more easily locate the cause of the ESP8266 reset.
When I use the sscanf approach, I don't get the reset, so something must be majorly wrong with my implementation of the pointer approach.
Maybe there's a problem with the code you didn't post.
The code I didn't post is commented out, leaving the barebones code I've posted here, so I don't think this is the issue. I'll test further and see if I can work out what the issue is. Thanks for your help.
int sscanf (const char *__buf, const char *__fmt,...)
There is no need to remove the start char(s) since sscanf() takes a buffer address -so-
what you give it for * __buf is *(array + 1) and it scans from the 2nd char to end.
Thank you GoForSmoke - so if I've understood correctly, it becomes:
void processCmd(char* rx) {
if (rx[0] == '[') {
float pos;
sscanf(&rx[2],"p=%f",&pos);
printf("pos=%f\n",pos);
}
}
I can tell you with 99% confident that this is the source of your crash:
processCmd((char *)payload);
Your "payload" is most certainly not a valid cstring (because there is an extra "length" parameter passed with it).
Try this
void processCmd(char* rx) {
if (rx[0] == '[') {
float pos;
sscanf(&rx[1],"p=%f",&pos);
printf("pos=%f\n",pos);
}
}
or use the array name as the pointer it is and add 1 to get the 2nd char in the array
void processCmd(char* rx) {
if (rx[0] == '[') {
float pos;
sscanf( (rx + 1),"p=%f",&pos);
printf("pos=%f\n",pos);
}
}
Both should compile the same.
rx is the address of the array, every element is rx plus an offset
(rx+0) is the same as rx[0] ----- the '[' is stored at rx
(rx+1) is the same as rx[1] ----- the next char is at rx+1
The brackets [] let us use arrays before we learn about pointers but using pointers tell us more about what an array is and how they work.
arduino_new:
I can tell you with 99% confident that this is the source of your crash:
processCmd((char *)payload);
Your "payload" is most certainly not a valid cstring (because there is an extra "length" parameter passed with it).
void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) {
char rxChars[128];
switch(type) {
case WStype_TEXT:
processCmd((char *)payload);
break;
}
}
payload is the name given to a passed byte (uint8_t is Arduino variable type byte) pointer.
processCmd((char *)payload) passes that byte pointer cast as a char pointer to processCmd().
Arduino variable type char is signed 8 bit int8_t which is why the cast to (char *) had to be made.
The pointer only stores address which is why you can cast one type as another, the cast tells the compiler use the address as you direct.
GoForSmoke:
void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) {
char rxChars[128];
switch(type) {
case WStype_TEXT:
processCmd((char *)payload);
break;
}
}
payload is the name given to a passed byte (uint8_t is Arduino variable type byte) pointer.
processCmd((char *)payload) passes that byte pointer cast as a char pointer to processCmd().
Arduino variable type char is signed 8 bit int8_t which is why the cast to (char *) had to be made.
The pointer only stores address which is why you can cast one type as another, the cast tells the compiler use the address as you direct.
Perhaps you are missing the point. Casting is not a problem. Casting an uint8_t* to and char* and treating that char* as a cstring is the problem.
sscanf(&rx[1],"p=%f",&pos);
nope.
printf("pos=%f\n",pos);
another nope.
Because it's not const char * but only char *.
And I did assume that payload is a byte array made from text read as bytes.
Why don't you spell out what you see wrong, crypto?