sbrm1 sbrm1 - 4 months ago 39
C++ Question

Splitting String in C++

What I Want:

To parse a MAC address string and get an array of six

uint8_t
values, having an input like
string str = "01:23:45:AB:CD:EF";
.

What I Tried (And Didn't Work):


sscanf(str.c_str(), "%x:%x:%x:%x:%x:%x", &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]);


Why it didn't work: I'm programming for an ESP8266 Wi-Fi Module with the help of the Arduino IDE. Even though the official cores for the Arduino board supports
sscanf()
, the cores for the ESP8266 don't. (The solution to the issue given on GitHub doesn't work, as stated by nicjohnston.)

Least Preferred:

Anything that uses up too much RAM, CPU cycles or program storage.
Also some stuff using or directly/indirectly depending on
sscanf
,
setjmp
,
longjmp
, (and several others) may not work.

Edit:
The answer by @m.s. below is absolutely correct, but I realized that after I had devised my own code (given below).

for(uint8_t i=0; i<5; i++)
mac[i]=strtol(Serial.readStringUntil(':').c_str(),NULL,16);
mac[6]=strtol(Serial.readStringUntil('\n').c_str(),NULL,16);

Answer

In your specific use case you can simply iterate over the string and convert the hex substrings to numbers like this:

  • each substring consists of exactly two hex characters; let a substring be denoted by XY
  • X and Y are either within [0-9] or within [A-F] where A means 10, B means 11, etc. (in decimal)
  • the transformation from an ASCII value to the numerical meaning of one of those characters depends on which character set a char is from:

  • if a char c is from [0-9], its decimal meaning is c-'0', i.e. substracting the offset so that the following mapping applies '0'=>0, '1' => 1, ...

  • if a char c is from [A-F], the mapping can be expressed as c-'A'+10

  • the numeric, decimal value v of each substring XY is then calculated as follows: v=X*16 + Y

  • the substrings are separated by a single char (here a :, but this does not matter)

The following code implements those ideas:


#include <string>
#include <cstdlib>

std::uint8_t getNum(char hexChar)
{
    if(hexChar >= '0' && hexChar <= '9')
    {
        return hexChar - '0';
    }
    return (hexChar-'A'+10);
}

int main()
{
    std::uint8_t mac[6];
    std::string str = "01:23:45:AB:CD:EF";

    std::uint8_t j = 0;
    for(std::uint8_t i = 0; i<6; ++i)
    {
        mac[i] = getNum(str[j])*16 + getNum(str[j+1]);
        j+=3; // get to the next substring
    }
}