C++ converting a mac id string into an array of uint8_t

13,858

Solution 1

I hate to answer this in this fashion, but sscanf() is probably the most succinct way to parse out a MAC address. It handles zero/non-zero padding, width checking, case folding, and all of that other stuff that no one likes to deal with. Anyway, here's my not so C++ version:

void
parse_mac(std::vector<uint8_t>& out, std::string const& in) {
    unsigned int bytes[6];
    if (std::sscanf(in.c_str(),
                    "%02x:%02x:%02x:%02x:%02x:%02x",
                    &bytes[0], &bytes[1], &bytes[2],
                    &bytes[3], &bytes[4], &bytes[5]) != 6)
    {
        throw std::runtime_error(in+std::string(" is an invalid MAC address"));
    }
    out.assign(&bytes[0], &bytes[6]);
}

Solution 2

Your problem may be in the output of the parsed data. The "<<" operator makes decisions on how to display data based on the data type passed it, and uint8_t may be getting interpretted as a char. Make sure you cast the array values to ints when printing, or investigate in a debugger.

The sample program:

uint8_t tgt_mac[6] = {0};
std::stringstream ss( "AA:BB:CC:DD:EE:11" );
char trash;

for ( int i = 0; i < 6; i++ )
{
    int foo;
    ss >> std::hex >> foo >> trash;
    tgt_mac[i] = foo;
    std::cout << std::hex << "Reading: " << foo << std::endl;
}

std::cout << "As int array: " << std::hex
    << (int) tgt_mac[0]
    << ":"
    << (int) tgt_mac[1]
    << ":"
    << (int) tgt_mac[2]
    << ":"
    << (int) tgt_mac[3]
    << ":"
    << (int) tgt_mac[4]
    << ":"
    << (int) tgt_mac[5]
    << std::endl;
std::cout << "As unint8_t array: " << std::hex
    << tgt_mac[0]
    << ":"
    << tgt_mac[1]
    << ":"
    << tgt_mac[2]
    << ":"
    << tgt_mac[3]
    << ":"
    << tgt_mac[4]
    << ":"
    << tgt_mac[5]
    << std::endl;

Gives the following output ( cygwin g++ )

Reading: aa
Reading: bb
Reading: cc
Reading: dd
Reading: ee
Reading: 11
As int array: aa:bb:cc:dd:ee:11
As unint8_t array: ª:»:I:Y:î:◄

Solution 3

First I want to point out that I think @Steven's answer is a very good one - indeed I noticed the same: the values are correct, but the output looks awkward. This is due to ostream& operator<<( ostream&, unsigned char ) being used, since the uint8_t type you used is a typedef for unsigned char (as I found in the linux man pages). Note that on VC++, the typedef isn't there, and you have to use unsigned __int8 instead (which will also route you to the char specialization).

Next, you can test your code like this (output-independent):

assert( uint8_t( parseHex( "00" ) ) == uint8_t(0) );
assert( uint8_t( parseHex( "01" ) ) == uint8_t(1) );
//...
assert( uint8_t( parseHex( "ff" ) ) == uint8_t(255) );

In addition to Steven's answer, I just want to point out the existence of the transform algorithm, which could still simplify your code.

for( int j = 0 ; j < v.size() ; j++ ){
  tgt_mac[j] = parseHex(v.at(j)); 
}

Writes in one line:

std::transform( v.begin(), v.end(), tgt_mac, &parseHex );

(And I know that hasn't to do with the question...)

(See codepad.org for what it then looks like)

Share:
13,858
Hamza Yerlikaya
Author by

Hamza Yerlikaya

Updated on June 29, 2022

Comments

  • Hamza Yerlikaya
    Hamza Yerlikaya almost 2 years

    I want to read a mac id from command line and convert it to an array of uint8_t values to use it in a struct. I can not get it to work. I have a vector of string for the mac id split about : and I want to use stringstream to convert them with no luck. What I am missing?

    int parseHex(const string &num){
      stringstream ss(num);
      ss << std::hex;
      int n;
      ss >> n;  
      return n;
    }
    
    uint8_t tgt_mac[6] = {0, 0, 0, 0, 0, 0};
      v = StringSplit( mac , ":" );
      for( int j = 0 ; j < v.size() ; j++ ){
        tgt_mac[j] = parseHex(v.at(j)); 
      }
    
  • Hamza Yerlikaya
    Hamza Yerlikaya over 15 years
    when i do a cout int the parse function i get correct values but when i assign them to the uint8_t array they get messed up. i tried your suggestion it works in the function but they still get messed up when i assign them.