A more elegant way...?

Hello. I want to check a received string and open a corresponding binary file from a SD card. Like this

    if (strcmp(receivedString, "one") == 0)  myFile = SD.open("one.bin");
    if (strcmp(receivedString, "two ") == 0)  myFile = SD.open("two.bin");

and so on, 50 times. The way I have done it works, but it is very memory hungry. I am sure there is a more elegant and more economical way of doing it, but it's beyond me. Any suggestions?

Why don't this once only?

char filename[30];
strcpy(filename, receivedString);
strcat(filename, ".bin");

File myFile = SD.open(filename);

if (!myFile) {
  Serial.println("Something wrong. Check filename");
}

....etc etc

Will the filename always match the string that you are testing for and how certain are you that receivedString will contain valid data ?

Do you have control over the string that is sent such that it could be "1", "2", etc ?

Thank you, that looks like the answer

Yes the filename will always match the string and receivedString will always have valid data
No, I can't (easily) change the string that is sent.

In that case use the solution suggested by @cotestatnt

strlcpy and strlcat are safer methods to use as they protect you against buffer overflows if the destination is too small
See strlcpy_strlcat.ino for an example sketch

They are simple to use

char filename[30];
strlcpy(filename, receivedString, sizeof(filename));
strlcat(filename, ".bin", sizeof(filename));

These will not crash you code if the result is too long , and you can easily check for errors

char filename[30];
if (strlcpy(filename, receivedString, sizeof(filename)) > sizeof(filename)) {
  // increase filename
  Serial.println(F(" filename size too small"));
}
if (strlcat(filename, ".bin", sizeof(filename))  > sizeof(filename)) {
  // increase filename
  Serial.println(F(" filename size too small"));
}

Edit - corrected strncpy to strlcpy etc while snprintf is the 'safe' version of sprintf, just to be confusing strncpy is NOT the safe version of strcpy :frowning:
Edit - fixed order of args in strlcpy strlcat

Actually a really simple and safe method is

File myFile;
{
String filename = receivedString;
filename += ".bin";
myFile = SD.open(filename);
}  // all String memory completely recovered here,  

no memory fragmenation
Edit @PieterP correctly points out due to the memory allocation in SD,open there will be a memory hole when String filename disposed of
thanks @PieterP

Not necessarily, SD.open() constructs a File object, which allocates:

Given that there at least 4 safe alternatives to strcpy/ strcat, there is really no excuse for using them.
The output is

Arduino String myFilename.bin
snprintf Error filename size too small.
SafeString Error: sfFileName = "myFilename"
 needs capacity of 10
        sfFileName cap:9 len:9 'myFilenam'
Error filename size too small.
strlcpy/strlcat Error filename size too small.
strcpy/strcat myFilename.bin  
<<  Undetected buffer overflow here 

// Alternatives to the 'unsafe' strcpy and strcat to build filenames, urls, msgs
#include <SD.h>
// only if using the SafeString alternative
#include "SafeString.h"

void setup() {
  Serial.begin(9600);
  for (int i = 10; i > 0; i--) {
    Serial.print(' '); Serial.print(i);
    delay(500);
  }
  Serial.println();

  char receivedString[] = "myFilename";
  char filename[10];
  File file;

  // Arduino String version
  {
    Serial.print(F("Arduino String "));
    String filename = receivedString;
    filename += ".bin";
    Serial.println(filename);
    file = SD.open(filename);
  } // String memory recovered here but get hole due to SD.open memory allocation

  // snprintf version
  Serial.print(F("snprintf "));
  if (snprintf(filename, sizeof(filename), "%s.bin", receivedString) > sizeof(filename)) {
    Serial.println(F("Error filename size too small."));
  } else {
    Serial.println(filename);
    file = SD.open(filename);
  }
  // SafeString version
  {
    Serial.print(F("SafeString "));
    SafeString::setOutput(Serial);
    createSafeStringFromCharArray(sfFileName, filename); // pick up the sizeof
    sfFileName = receivedString;
    sfFileName += ".bin";
    if (sfFileName.hasError()) {
      Serial.println(F("Error filename size too small."));
    } else {
      Serial.println(filename);
      file = SD.open(filename);
    }
  } // SafeString memory recovered here

  // strlcpy strlcat version
  Serial.print(F("strlcpy/strlcat "));
  if (strlcpy(filename, receivedString, sizeof(filename)) > sizeof(filename)) {
    Serial.println(F("Error filename size too small."));
  } else {
    if (strlcat(filename, ".bin", sizeof(filename)) > sizeof(filename)) {
      Serial.println(F("Error filename size too small."));
    } else {
      Serial.println(filename);
      file = SD.open(filename);
    }
  }

  // unsafe strcpy strcat version
  Serial.print(F("strcpy/strcat "));
  strcpy(filename, receivedString);
  strcat(filename, ".bin");
  Serial.println(filename);
  file = SD.open(filename);
  Serial.println(F(" Undetected buffer overflow here "));
}
void loop() {
}
2 Likes

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