How to split a string with space and store the items in array?

So I have:

String str= "A1N c2 t120 d4 t0 b5 t0 a2 t368 e2 t452 1c t0 e1 t600";

how can I convert this into array of substring which would look like :

str[0]=A1n
str[1]=c2
str[3]=t120
str[4]=d4
str[5]=t0.. and so on until t600

Hi @,
use subString().
myString.substring(from, to)
Ref:
https://www.arduino.cc/reference/en/language/variables/data-types/string/functions/substring/

But how will I separate it where there is space ? if I use substring I won't know where to end

Use str.length() to know for String lenght.

I am not a user of Strings, but there is an indexOf() function that will allow you to find the spaces in the String so you can use the value as one of the parameters of subString() ?

consider code below producing

A1N c2 t120 d4 t0 b5 t0 a2 t368 e2 t452 1c t0 e1 t600
A1N
c2
t120
d4
t0
b5
t0
a2
t368
e2
t452
1c
t0
e1
t600

#define SPTR_SIZE   20
char   *sPtr [SPTR_SIZE];

int
separate (
    String str,
    char   **p,
    int    size )
{
    int  n;
    char s [100];

    strcpy (s, str.c_str ());

    *p++ = strtok (s, " ");
    for (n = 1; NULL != (*p++ = strtok (NULL, " ")); n++)
        if (size == n)
            break;

    return n;
}

String  str= "A1N c2 t120 d4 t0 b5 t0 a2 t368 e2 t452 1c t0 e1 t600";

void
setup (void) {
    Serial.begin (9600);

    Serial.println (str);

    int N = separate (str, sPtr, SPTR_SIZE);
    for (int n = 0; n < N; n++)
        Serial.println (sPtr [n]);
}

void
loop (void) {
}

@sericsheon do you have to use a String (capital S) or could you use a C style string (lowercase s) ?

You should be very careful with using the "String" class (especially on AVR based boards) since it uses dynamic memory in a non-transparent way and this may lead to multiple issues, including crashes. Instead of using a string for each of the tokens, you could get the tokens by index:

bool get_token(String &from, String &to, uint8_t index, char separator)
{
  uint16_t start = 0, idx = 0;
  uint8_t cur = 0;
  while (idx < from.length())
  {
    if (from.charAt(idx) == separator)
    {
      if (cur == index)
      {
        to = from.substring(start, idx);
        return true;
      }
      cur++;
      while ((idx < from.length() - 1) && (from.charAt(idx + 1) == separator)) idx++;
      start = idx + 1;
    }
    idx++;
  }
  if ((cur == index) && (start < from.length()))
  {
    to = from.substring(start, from.length());
    return true;
  }
  return false;
}

void setup()
{
  Serial.begin(9600);
  String tokens = "A1N c2 t120 d4 t0 b5 t0 a2 t368 e2 t452 1c t0 e1 t600", token;
  uint8_t token_idx = 0;
  while (get_token(tokens, token, token_idx, ' '))
  {
    Serial.print("Token[");
    Serial.print(token_idx);
    Serial.print("] = \"");
    Serial.print(token);
    Serial.println("\"");
    token_idx++;
  }
}

void loop()
{
}

Preferably you should use "strtok" with a char array instead og "String", though!

String  str = "A1N c2 t120 d4 t0 b5 t0 a2 t368 e2 t452 1c t0 e1 t600";
String strs[20];
int StringCount = 0;

void setup (void)
{
  Serial.begin (115200);
  delay(200);
  
  Serial.println(str);

  // Split the string into substrings
  while (str.length() > 0)
  {
    int index = str.indexOf(' ');
    if (index == -1) // No space found
    {
      strs[StringCount++] = str;
      break;
    }
    else
    {
      strs[StringCount++] = str.substring(0, index);
      str = str.substring(index+1);
    }
  }

  // Show the resulting substrings
  for (int i = 0; i < StringCount; i++)
  {
    Serial.print(i);
    Serial.print(": \"");
    Serial.print(strs[i]);
    Serial.println("\"");
  }
}

void loop () {}
A1N c2 t120 d4 t0 b5 t0 a2 t368 e2 t452 1c t0 e1 t600
0: "A1N"
1: "c2"
2: "t120"
3: "d4"
4: "t0"
5: "b5"
6: "t0"
7: "a2"
8: "t368"
9: "e2"
10: "t452"
11: "1c"
12: "t0"
13: "e1"
14: "t600"

thanks soo much it works, also can access these elements with for loop in void loop() as well? also is there a way to compare each element of the of these substrings with each other ? like in str[2]:

time=millis();
if (str[i][0]==t);
    if (time<= 120):     // someway to get these digits and ignore t
        do something...

Yes. String strs[20]; and int StringCount; are both global variables so you can use them in loop() or any other function in your sketch.

Yes. if (strs[i] == strs[j]) will compare two strings from the list. The values i and j must be from 0 to StringCount-1.

You can strip off the first character with .substring(1) and you can convert the resulting String from digit characters to a number with .toInt()(or .toFloat() if there is a decimal point).

int t_val = (strs[i].substring(1)).toInt();

1 Like

Why are you saying that String is a class? It is a user-defined data type that has been created somewhere else using class keyword. I would rather prefer to say "String" data type.

NO no crashes see Taming Arduino Strings for why String is very very safe on AVR board

This makes String a class, doesn't it?

The idea of using the result of strtok is great but there are a few problem with the solution.

That statement is very prone to buffer overruns.
The newer safer method is strncpy(s,str.c_str(),sizeof(s));
which prevents buffer overruns.
see strlcpy_strlcat.ino

But a more serious problem with the solution is that the 'answer' becomes invalid once the String str goes out of scope, i.e. at the end of the separate( ) method (since the input String is copied, not referenced, in the method call)

One way to fix both these problems is to use strdup()

//https://forum.arduino.cc/t/how-to-split-a-string-with-space-and-store-the-items-in-array/888813/6
#define SPTR_SIZE   20
char   *sPtr [SPTR_SIZE]; // this is reused each call
char *strData = NULL; // this is allocated in separate and needs to be free( ) eventually
size_t numberOfStr = 0;  // number of valid elements in sPtr[  ]

// use this free the memory after use
void freeData(char **pdata) {
  free(*pdata);
  *pdata = NULL;
  numberOfStr = 0;
}

int separate (String& str,  // pass as a reference to avoid double memory usage
              char **p,  int size, char **pdata ) {
  int  n = 0;
  free(*pdata); // clean up last data as we are reusing sPtr[ ]
  // BE CAREFUL not to free it twice
  // calling free on NULL is OK
  *pdata = strdup(str.c_str()); // allocate new memory for the result.
  if (*pdata == NULL) {
    Serial.println("OUT OF MEMORY");
    return 0;
  }
  *p++ = strtok (*pdata, " ");
  for (n = 1; NULL != (*p++ = strtok (NULL, " ")); n++) {
    if (size == n) {
      break;
    }
  }
  return n;
}

String  str1 = "A1N c2 t120 d4 t0 b5 t0 a2 t368 e2 t452 1c t0 e1 t600";
String  str2 = "t0 a2 t368 e2 t452 1c t0 e1 t600";

void
setup (void) {
  Serial.begin (9600);
  for (int i = 10; i > 0; i--) {
    Serial.print(' '); Serial.print(i);
    delay(500);
  }
  Serial.println();
  strData = NULL; //make sure it is NULL to start with
}

void loop (void) {
  String str;
  int N = 0;
  str = str1;
  Serial.print(F("input str:")); Serial.println(str);
  N = separate (str, sPtr, SPTR_SIZE,  &strData);
  for (int n = 0; n < N; n++) {
    Serial.println (sPtr [n]);
  }
  freeData(&strData);

  str = str2;
  / Serial.print(F("input str:")); Serial.println(str);
  N = separate (str, sPtr, SPTR_SIZE,  &strData);
  for (int n = 0; n < N; n++) {
    Serial.println (sPtr [n]);
  }
  freeData(&strData);
  delay(6000);
}

But class with "lowercase c" is a keyword which allows us to declare a conglomerate data type that contains both "data" and "functions" to process that data as well the mechanism of "protection". We give a tag to the "declared database"; where, the tag is the "user-defined data type" from which we create objects on which we apply various methods.

how did i know you would point this out

Easy, you know I am a big fan of robust programming.

we had a course on this at qualcomm to deal with hackers sending extra long data into the phone attempting to cause it to reveal internal information.

the need was to protect against diabolical messages. we didn't need to do this for internal code.

In that case, you would cease your "String" crusade and advice ppl to use statically allocated buffers which would allow a transparent memory usage that never changes during runtime.

1 Like