Using strings instead of Strings

Maybe my problem is simple, but it's hard for me.

I like programming, but I have limited programming knowledge.
I have a relatively large project in which I use the "String" format for many variables.

I often see comments recommending not using Strings for technical reasons.
So I decided to change my Strings to strings.
(strings: I think that's how they refer to variables of the const char* xxxx type) Is that correct?

I'm going to post a very simple code here that reflects my problem:
I have this:

String timerOn[] {"10:15:32", "14:22:47", "17:19:12"};

and I separate the values ​​of hours, minutes and seconds like this:

hours = (timerOn[i].substring(0, 2)).toInt();
minutes = (timerOn[i].substring(3, 6)).toInt();
seconds = (timerOn[i].substring(6, 8)).toInt();

but I can't separate the values ​​using this:

const char* timerOn[] {"10:15:32", "14:22:47", "17:19:12"};

I can only get the values ​​of each index of timerOn[].

I appreciate any help that teaches me how to separate the values ​​of hours, minutes and seconds
from the content of each index of timerOn[].

Here is what I wrote using String and string.

Of course the values ​​printed by string were "forced", because I don't know how to put them in a variable.

Below is also a copy of the code I used in wokwi.

Tks for all


//-----------------------------------------------------------------
void setup() {
  Serial.begin(115200);
  delay(100);
  my_old();
  Serial.println("");
  my_new();
}
//-----------------------------------------------------------------
void loop() {}
//-----------------------------------------------------------------
void my_old() {
  String timerOn[]  {"10:15:32", "14:22:47", "17:19:12"};
  int hours;
  int minutes;
  int seconds;
  for (int i = 0; i < 3; i++) {
    Serial.println(timerOn[i]);
  }
  for (int i = 0; i < 3; i++) {
    hours = (timerOn[i].substring(0, 2)).toInt();
    minutes = (timerOn[i].substring(3, 6)).toInt();
    seconds = (timerOn[i].substring(6, 8)).toInt();
    Serial.print("hours: " ); Serial.print(hours);
    Serial.print("  minutes: " ); Serial.print(minutes);
    Serial.print("  seconds: " ); Serial.println(seconds);
  }
}
//-----------------------------------------------------------------
void my_new() {
  const char* timerOn[]  {"10:15:32", "14:22:47", "17:19:12"};
  for (int i = 0; i < 3; i++) {
    Serial.println(timerOn[i]);
  }
  Serial.print("hours: " ); Serial.print(10);
  Serial.print("  minutes: " ); Serial.print(15);
  Serial.print("  seconds: " ); Serial.println(32);
  Serial.print("hours: " ); Serial.print(14);
  Serial.print("  minutes: " ); Serial.print(22);
  Serial.print("  seconds: " ); Serial.println(47);
  Serial.print("hours: " ); Serial.print(17);
  Serial.print("  minutes: " ); Serial.print(19);
  Serial.print("  seconds: " ); Serial.println(12);
}

See:
https://cplusplus.com/reference/cstring/
https://en.cppreference.com/w/cpp/header/cstring

1 Like

I don't use Wokwi. I don't see any time libraries. What you need to do is all done in the various libraries. NOTE: some of this time and date stuff is not so easy, but with perseverance and testing can be figured out. Good luck.

Why use Strings or strings ?

//String timerOn[] {"10:15:32", "14:22:47", "17:19:12"};

struct timeData
{
    byte hour;
    byte min;
    byte sec;
};

timeData timeTable[3] = {
    { 10, 15, 32 },
    { 14, 22, 47 },
    { 17, 19, 12 }
};

void setup()
{
    Serial.begin(115200);
    for (int event = 0; event < 3; event++)
    {
        Serial.print("event # ");
        Serial.print(event);
        Serial.print(" hour: ");
        Serial.print(timeTable[event].hour);
        Serial.print(" min: ");
        Serial.print(timeTable[event].min);
        Serial.print(" sec: ");
        Serial.print(timeTable[event].sec);

        Serial.println();
    }
}

void loop()
{
}

2 Likes

I use String class objects on microcontrollers which have sufficient SRAM to support them, e.g. STM32, ESP32, RP2040, etc
not recommended on microcontrollers with low SRAM, e.g. UNO , Mega, classic Nano, etc - String can fragment memory causing system crashes

look this over


int hr;
int min;
int sec;

char s [90];

// -----------------------------------------------------------------------------
void
getHrMinSec (
    const char *time )
{
    sscanf (time, "%d:%d:%d", &hr, &min, &sec);

    sprintf (s, " hr %d, min %2d, sec %2d", hr, min, sec);
    Serial.println (s);
}

void
loop (void)
{
    if (Serial.available ())  {
        char buf [0];
        int n = Serial.readBytesUntil ('\n', buf, sizeof(buf)-1);
        buf [n] = '\0';         // terminate with nul
        getHrMinSec (buf);
    }
}

void
setup (void)
{
    Serial.begin (9600);
    getHrMinSec ("10:15:32");
}
1 Like

If a Demo-Code shall be easy to understand use longer variable names, expand and vary patterns to make it easy to see what the generalised pattern is.

int hour;
int minute;
int second;

char my_cstring [90];

void setup () {
    Serial.begin (115200);
    Serial.println("Setup-Start ");
    Serial.println("enter a time in this format HH:MM:SS");
    getHrMinSec ("10:15:32");
}


void getHrMinSec ( const char *time ) {
    sscanf (time, "%d:%d:%d", &hour, &minute, &second);

    // stat with well known pattern
    Serial.print("hour=#");
    Serial.print(hour);
    Serial.print("# ");
    Serial.print("minute=#");
    Serial.print(minute);
    Serial.print("# ");
    Serial.print("second=#");
    Serial.print(second);
    Serial.print("#");
    Serial.println();

    // showing the generalised pattern of sprintf by expanding /varying the pattern
    sprintf (my_cstring, "hour %d" ,hour);
    Serial.println (my_cstring);

    sprintf (my_cstring, "hour %d, minute %2d", hour, minute);
    Serial.println (my_cstring);

    sprintf (my_cstring, "hour %d, minute %2d, second %2d", hour, minute, second);
    Serial.println (my_cstring);
}

void loop () {
    if (Serial.available ())  {
        char receivebuffer [90];
        int n = Serial.readBytesUntil ('\n', receivebuffer, sizeof(receivebuffer)-1);
        receivebuffer [n] = '\0';         // terminate with nul
        getHrMinSec (receivebuffer);
    }
}
1 Like

C is case-sensitive, and using the code font can clarify

  • string: a bunch of characters, uh, stringed together to make words, sentences, data, etc
  • P-string: well how many characters are there? One way is to use a byte for the length at the front, or prefixing; e.g. "cat" would in (ASCII, hex) 03 63 61 74. This is how Pascal does it. However, this limits string length to 255.
  • C-string: use a specific character as a terminator instead: ASCII zero.
    • All characters before space, ASCII 0x20 (decimal 32), are control characters: used in the old days to move the head on a teletype machine or indicate stuff. They all have two- or three-letter uppercase abbreviations (erroneously referred to in the spec as "acronyms"): BS backspace (0x08), ESC escape (0x1B), SOH Start of Header (0x01), etc.
    • 0x00 is NUL, short for Null, similar to NULL in C (which itself was replaced by nullptr in C++)
    • So "cat" is 63 61 74 00. You can have strings of any length, but to find that length, you have to scan the whole thing looking for the NUL; and if it's not where it is supposed to be for whatever reason, bad things happen.
    • That's referenced with a char*, a pointer to that first character. Many functions also take a length just to obviate scanning.
      • In C, an array is also a pointer, so char yourname[] is also used
        • An array also has a size/length, but only when referenced directly. If you pass an array as a function argument -- even to one declared as an array like arg[] -- it "decays" to a pointer, losing the ability to get the length of the original array; you get the size of a pointer instead (always 2 or 4 or whatever).
    • The const is to tell the compiler that nothing is allowed to modify those characters through that pointer. If you have "cat" in your program, and some function changes the bytes there to "dog", then you know: mass hysteria.
  • std::string or just string: All that is a big source of errors, so let's use objects to encapsulate the data and length together. What eventually becomes the C++ Standard Library has string, with lots of features.
  • String: too many features, so Arduino has its own trimmed down class. To my eye, the Arduino one might have been copying JavaScript, which copied Java's
    • String can cause heap fragmentation, which you can reduce to some degree with reserve
    • Some implementations, like on ESP32, do SSO, Small String Optimization, which reduces heap fragmentation at the cost of using more memory. Small strings -- a dozen(?) bytes or smaller -- don't do their own heap allocation. They are stored "inside" the larger object itself: on the stack as local variables, or in the global variable space.

I appreciate everyone's attention, and I will study the recommendations.
For now, I will try to apply the solution presented by @gcjr.

That's a bit misleading.

In C, an array name can be used as a pointer to its first element. An array is not a pointer.

What you may be informally suggesting is the wonderful truth that in C, the name of an array is implicitly converted to a pointer to its first element in many contexts.

An example which if understood might help nail down some differences

const int nElements = sizeof someArray / sizeof *someArray;

The type of the pointer that someArray becomes is pointer to the type of the array element.

a7

Why would you use Strings, or strings for that matter, for data that is, by its very nature, numeric ?

As an interesting side note:

	char stringArray[10];

	auto pointer1 = stringArray;
	auto pointer2 = &stringArray;

pointer1 and pointer2 are two different data types. pointer1 is a pointer to a char (i.e. char *). pointer2 is a pointer to an array of 10 char (i.e. char (*)[10])

2 Likes

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.