Copy part of a char* to another char*

I’m having a weird problem to copy the part of a char* to another char*, it looks like the copy is changing the contents of the source char*.
This is part of my code:

void setValuesParamsList(char* bluetoothString) {

  int lastPosition = 0;
  int endPosition = 0;
  boolean param = false;
  boolean value = false;
  int lengthChar = 0;
  char* paramString;
  char* valueString;

  Serial.println(bluetoothString);
  
  for(int i=0; i<strlen(bluetoothString); i++) {
    Serial.println("Percorrendo");

    // procura pelo '='
    if(bluetoothString[i] == '=') {
      Serial.println("->Param");
      endPosition = i-1;
      param = true;
      value = false;
    } else if(bluetoothString[i] == '#' || i == (strlen(bluetoothString)-1)) {
      Serial.println("->Value");
      endPosition = bluetoothString[i] == "#" ? i-1 : i;
      param = false;
      value = true;
    }

    if(param == true && value == false) {
      lengthChar = endPosition - lastPosition;
      delete[] paramString;
      paramString = new char(lengthChar+1);
      
      int l=0; // contador para popular o char*
      for(int j=lastPosition; j<=endPosition; j++) {
        // insere os dados no vector
        paramString[l++] = bluetoothString[j];
      }

      paramString[lengthChar+1] = '\0';
      Serial.println(F("-------------------------------"));
      Serial.println(paramString);
      Serial.println(String(strlen(paramString)));
      Serial.println(bluetoothString);
      Serial.println(String(strlen(bluetoothString)));
      Serial.println(F("-------------------------------"));
      lastPosition = i+1;
      param = false;
      value = false;
    }
    
  }

  
}

This is what appears on the serial monitor:

action=getData#time=111111

Percorrendo
Percorrendo
Percorrendo
Percorrendo
Percorrendo
Percorrendo
Percorrendo
->Param
-------------------------------
action
6
on
2
-------------------------------

The idea is to read the parameters and values of the parameters from char * “action=getData#time=111111”, but it seems that the copy of part of the char * affects the original value and stops the main FOR.

Am I copying the snippet the wrong way?

void setValuesParamsList(char* bluetoothString) {
...
  char* paramString;
...
      delete[] paramString;

paramString is uninitialized. You've just corrupted the heap.

You are off by one in the copy loop.

I just put it to test and forgot to remove it, at least it does not seem to have affected!

Sorry did not understand!

Start here...

    if(param == true && value == false) {
      lengthChar = endPosition - lastPosition;

Work your way through the code. Assuming endPosition is equal to lastPosition simplifies the process. I wasn't paying much attention beyond "there is a mistake" but I believe your code overruns paramString.

I replaced new char(varLength) with new char(10) to see if it was the size that was being set, but the problem persisted. Then I decided to start the variables with new char() (without value in char) and inside the IF/ELSE I make a new char(varLength) and it works!
Now I have a problem where whenever I try to make a delete[] variable the system gets lost again.

I’m surprised to have to start with new char() since I’ve already used pointer vector on other systems and I did not need that and delete[] already worked! :astonished:

Now my code looks like this:

  int lastPosition = 0;
  int endPosition = 0;
  boolean param = false;
  boolean value = false;
  int lengthChar = 1;

  char* paramString = new char();
  char* valueString = new char();

  Serial.println(bluetoothString);
  
  for(int i=0; i<strlen(bluetoothString); i++) {
    // procura pelo '='
    if(bluetoothString[i] == '=') {
      endPosition = i-1;
      param = true;
      value = false;
    } else if(bluetoothString[i] == '#' || i == (strlen(bluetoothString)-1)) {
      endPosition = bluetoothString[i] == '#' ? i-1 : i;
      param = false;
      value = true;
    }

    if(param == true && value == false) {
      lengthChar = endPosition - lastPosition;
      
      paramString = new char(lengthChar);
      
      int l=0;
      for(int j=lastPosition; j<=endPosition; j++) {
        // insere os dados no vector
        paramString[l++] = bluetoothString[j];
      }

      paramString[lengthChar+1] = '\0';
      lastPosition = i+1;
    } else if(param == false && value == true) {
      lengthChar = endPosition - lastPosition;
      
      valueString = new char(lengthChar);

      int l=0;
      for(int j=lastPosition; j<=endPosition; j++) {
        // insere os dados no vector
        valueString[l++] = bluetoothString[j];
      }

      valueString[lengthChar+1] = '\0';            
      lastPosition = i+1;
    }

    if(param == true || value == true) {      
      param = false;
      value = false;
    }
    
    if(strlen(paramString) > 0 && strlen(valueString) > 0) {
      // not working...
      delete[] paramString;
      delete[] valueString;
    }
    
  }

Work from statically allocated char arrays

If your bluetoothString is action=getData#time=111111

then a

char * ptrFirstEqual = strchr (bluetoothString, ‘=‘); //http://www.cplusplus.com/reference/cstring/strchr/
char * ptrFirstHash = strchr (bluetoothString, ‘#‘);

would find pointers to ‘=‘ and ‘#’ within your bluetoothString

Then use strncpy() and math on pointer to bring the substring into memory

const size_t maxBuffLength = 15;
char actionBuffer[maxBuffLength+1]; // allocate local buffer with space for trailing null char
if (ptrFirstEqual && ptrFirstHash && (ptrFirstHash > ptrFirstEqual)) {
   size_t actionLength = ptrFirstHash-ptrFirstEqual-1;
   if (actionLength <= maxBuffLength) {
      strncpy(actionBuffer, ptrFirstEqual+1, actionLength);// http://www.cplusplus.com/reference/cstring/strncpy/
      actionBuffer[actionLength] = ‘\0’; // properly terminate the c-string
   } else {
      // handle buffer too small
   }
} else {
   // handle Wrong Input 
}

(code typed here so might have typos)

Then you can continue searching from ptrFirstHash+1 to get in a similar way the rest of the data.

If it’s OK to mess around with the content of bluetoothString you could also use the strtok() function to parse

See standard c-string functions in stdlib.h and string.h

andresilva:
Now my code looks like this:

Still off by one. Still corrupting the heap. (Now you have two off-by-one mistakes. You’re headed in the wrong direction.)

@J-M-L is dispensing good advice. Follow it.

J-M-L:
Work from statically allocated char arrays

If your bluetoothString is action=getData#time=111111

then a

char * ptrFirstEqual = strchr (bluetoothString, ‘=‘); //http://www.cplusplus.com/reference/cstring/strchr/

char * ptrFirstHash = strchr (bluetoothString, ‘#‘);



would find pointers to ‘=‘ and ‘#’ within your `bluetoothString`

Then use [strncpy()](http://www.cplusplus.com/reference/cstring/strncpy/) and math on pointer to bring the substring into memory


const size_t maxBuffLength = 15;
char actionBuffer[maxBuffLength+1]; // allocate local buffer with space for trailing null char
if (ptrFirstEqual && ptrFirstHash && (ptrFirstHash > ptrFirstEqual)) {
  size_t actionLength = ptrFirstHash-ptrFirstEqual-1;
  if (actionLength <= maxBuffLength) {
     strncpy(actionBuffer, ptrFirstEqual+1, actionLength);// strncpy - C++ Reference
     actionBuffer[actionLength] = ‘\0’; // properly terminate the c-string
  } else {
     // handle buffer too small
  }
} else {
  // handle Wrong Input
}



(code typed here so might have typos)


Then you can continue searching from `ptrFirstHash+1` to get in a similar way the rest of the data.

If it’s OK to mess around with the content of `bluetoothString` you could also use the [strtok()](http://www.cplusplus.com/reference/cstring/strtok/) function to parse

See standard c-string functions in [stdlib.h](http://www.cplusplus.com/reference/cstdlib/) and [string.h](http://www.cplusplus.com/reference/cstring/)

Thank you T-M-L!
It helped a lot, I did not know this way of working with pointers, I do not have much experience with them.

Coding Badly, thanks for the tips and attention!

Glad it helped :slight_smile:

Understanding pointers on small micro-controllers is a good skill to invest in

Work from statically allocated char arrays

As an alternative to the pointer managment and string functions, you can use sscanf to parse the null terminated bluetoothString into null terminated statically allocated substrings.

The idea is to read the parameters and values of the parameters from char * "action=getData#time=111111"

I'm not clear on how the bluetoothString varies, and what you want for substrings("parameters and values"), but it from the previous postings I think you want string between the = and the #("getData"), and the string following the #("time=111111"). Or perhaps you want the string following the #("time") and the numbers after = (111111) as an integer?

Here's an example of of the bluetoothString parsed into four substrings with sscanf. The numerical string can be turned into an integer with atoi if thats what you need.

Please explain more about how you want to parse the bluetoothString.

Normally, sscanf is used with blank spaces as separators, but with the use of the % string format specifier with a character exclusion set[^] you can use sscanf to parse strings with other separators into null terminated substrings.

void setup() {
  Serial.begin(115200);
  char* bluetoothString = "action=getData#time=111111";
  Serial.println(bluetoothString);
  
  char str1[20] = "";//size substrings appropriately
  char str2[20] = "";
  char str3[20] = "";
  char str4[20] = "";
  
  sscanf(bluetoothString, "%[^=] = %[^#] # %[^=] = %s", &str1, &str2, &str3, &str4);

  Serial.println(str1);
  Serial.println(str2);
  Serial.println(str3);
  Serial.println(str4);
  Serial.println(bluetoothString);//original string is unmodified
}

void loop() {}

Prints

action=getData#time=111111
action
getData
time
111111
action=getData#time=111111

You do not have to assign all the fields.

J-M-L:
Glad it helped :slight_smile:

Understanding pointers on small micro-controllers is a good skill to invest in

Understanding pointers is necessary, regardless of what platform you are programming on.

PaulS:
Understanding pointers is necessary, regardless of what platform you are programming on.

fair (even if your programing language does not have any such concept exposed to the user)

cattledog:
As an alternative to the pointer managment and string functions, you can use sscanf to parse the null terminated bluetoothString into null terminated statically allocated substrings.

I tend to stay away from sscanf() or sprintf() as they bring in 1.7kB of additional code. So use with care if program space is getting low and you can get away with a simple parser

I posted this in the french forum recently

If you look at this program:

char message[] = "DATA=123\r\n";

void setup() {
  int value;
  Serial.begin(115200);
  sscanf(message, "DATA=%d", & value); // http://www.cplusplus.com/reference/cstdio/sscanf/
  Serial.println(value);
}

void loop() {}

Compilation (on aUNO here) gives

Sketch uses 3562 bytes (11%) of program storage space

If you use this code

char message[] = "DATA=123\r\n";

void setup() {
  int value;
  Serial.begin(115200);
  value = atoi(message + 5); // http://www.cplusplus.com/reference/cstdlib/atoi/?kw=atoi
  Serial.println(value);
}

void loop() {}

Compilation (UNO) says

Sketch uses 1822 bytes (5%) of program storage space

–>Using sscanf() costs 1740 bytes of program memory

of course you need to handle errors, which is not done above.

You can with a bit more work write your own dedicated parser

char message[] = "DATA=123\r\n";

boolean lireData(int &data) // means readData in French
{
  boolean erreur = true;
  size_t i = 0;
  while ((message[i] != '\0') && (message[i] != '=')) i++; // looking for '='
  if (message[i] == '=') { // if we found it
    i++; // skip it
    if ((message[i] >= '0') && (message[i] <= '9')) { // if it's a digit
      data = 0; // initialize your data
      erreur = false; // we know we will get something correct as there is a number to parse
      while ((message[i] != '\0') && (message[i] >= '0') && (message[i] <= '9')) {
        data = data * 10 + (message[i++] - '0'); // build up the integral number (we don't deal with overflow)
      }
    }
  }
  return erreur;
}

void setup() {
  int valeur = 0;
  Serial.begin(115200);
  if (!lireData(valeur)) Serial.println(valeur);
}

void loop() {}

Here you actually achieved the same result and even save a bit more program memory (44 bytes ! :-)):

Sketch uses 1778 bytes (5%) of program storage space.

of course it means a bit more work to do

if memory is not a problem, then using the “easy” solution is not wrong of course

I used strchr with while to get the values in the vector to make the most of memory!

Thank you guys!