Creating, Reading and Editing strings

Sorry to ask something so simple, but I wonder if someone can point me in the right direction. I just want to create and read from, and edit, text strings (no need for PROGMEM). Just simple text strings.

char *Alpha[] = { "A", "B", "C", };
So far so good. I can even read from it okay:

if (Alpha[0]=="A") // Dot Dash { sound.tone(NOTE_C5,100, NOTE_REST, 100, NOTE_C5,400); }

Is there a way to insert letters like into the string like: Alpha[0] = "A"; ?

Thanks!

Use single quotes for single ASCII characters:
Alpha[0] = 'A';

Double quotes create null terminated ASCII strings.

3 Likes

Oh nice, I’ll do that thanks!

Also when using the single quotes for single characters you don’t need char *Alpha[] = { 'A', 'B', 'C'}; instead use char Alpha[] = { 'A', 'B', 'C'}; Then you should easily be able to manipulate individual letters with Alpha[0] = 'D' or something.

3 Likes

Ah, I’m not 100% sure if you intended it, but the declaration you wrote for Alpha makes an array of strings[a]. If you want a string that stores a singular name as the user writes it out, (like a high-score list name or just any old text input) you probably just want to allocate an array with a specific size, and you’d do that with the syntax char *Alpha[8] for a string with space for 7 characters and an end-of-string marker.

[a]: cdecl is a good tool for decoding type signatures; and strings are just pointers to char arrays in C

Why “with a specific size”?

Most programming languages’ string libraries are built upon using dynamic allocation (i.e. finding space for variables while the program is running) to get a chunk of memory to store a string’s data. As more characters are added to the string, some library function will automatically “grow” the string (i.e. find a new larger chunk of memory and copy-paste the old chunk’s contents to that new place).

The Arduboy is so absurdly small that doing these dynamic allocations can eat up a lot of your available program space, from the sheer act of the compiler bringing in an allocator and asking the program to find space on the Arduboy to store a heap. Plus, it might be outright slower to keep finding new chunks of memory. Because of this, most developers (or at the very least, me) forgo it in favor of defining every structure before the program is run.

What size should I pick?

I dunno!! 64 characters or something? Maybe even make it a compile-time constant with #define, so you can easily change it later:

#define TEXT_INPUT_LEN 64
char Alpha[TEXT_INPUT_LEN] = { '\0' };

Just make sure to check that the user’s input never exceeds that number of characters, or you’ll bump up against (and possibly corrupt) other variables in your program. (Strings typically take up 1 more character than expected, as they need to store an end-of-string marker – typically a character that is equal to ASCII 0 – you can write it in C++ with '\0'.)

Adding Characters to the String

Pseudocode: Find the '\0' end-of-string marker in the array, and write characters into the array starting at that point. Once done, add the '\0' marker to the end again.

This is written in a C coding style, and is probably not the best fit for Arduboy’s C++, but it’ll work.

// this function is similar to C's `strnlen` function
// and you should probably use that instead!!
uint8_t find_end_of_string(char *string, uint8_t max_length) {
  for (uint8_t i = 0; i < length; i++)
    if (string[i] == '\0')
      return i;
  return max_length - 1;
}

void add_chars_to_string(char *string, char *more_chars) {
  // first, find where the string's end-of-string marker is
  uint8_t end_marker = find_end_of_string(string, TEXT_INPUT_LEN);

  // with that marker we just found as the starting index,
  // add characters from the more_chars string to the first string.
  uint8_t current_char = 0;
  while ( // we haven't yet reached the end of more_chars,
    more_chars[current_char] != '\0'
  ) {
    // cancel the copy if we just ran out of space
    // to put the end-of-string marker byte back in.
    if (end_marker + current_char + 1 >= TEXT_INPUT_LEN) return;
    
    // copy over a character from the second string into the first!
    string[end_marker + current_char] = more_chars[current_char];
    current_char++;
  }

  // conveniently, the previous loop we did left behind the end_marker's
  // next valid position, so we can set the marker with two simple lines:
  end_marker = end_marker + current_char;
  string[end_marker] = '\0';
}

Removing Single Characters from the String (“backspace”)

Pseudocode: move the end-of-string marker back one byte, unless it would go outside the string’s bounds.

void backspace_string(char *string) {
  uint8_t end_marker = find_end_of_string(string, TEXT_INPUT_LEN);
  if (end_marker == 0) return; // oops! no more to backspace!
  string[end_marker - 1] = '\0'; // replace last character with end-of-string marker.
}

And I’m out of time to keep writing this post, hah… Hope this helped!

1 Like

A few things to point out:

Two things:

constexpr size_t alphaCapacity = 64;

char alpha[alphaCapacity] {};

That function is missing a return. If i ever reaches length the loop will end, and there’s no return after the loop, which means the function has undefined behaviour if the provided string isn’t properly null-terminated.

Aside from which, you can just use strnlen(string, max_length), which does the same job.
(All AVR Arduino boards, including Arduboy, use avr-libc for their C standard library implementation.)

Similarly, add_chars_to_string is doing basically the same thing as strcat(destination, source), except that it doesn’t return a pointer to the new end of the string, so it’ll run into the ‘schlemiel the painter’ problem.

(There’s also the slightly safer strlcat.)

2 Likes

a few of those things were on purpose to not bog down people with irrelevant details / keyboard-bash names (i cut a section about the existing program’s string interning for this reason) (and i ran out of time to look over and edit the post (as this was a distraction from studying), which is why that first one was missing a return, ha… it’s fixed now.)

2 Likes