3

I have a hex string and want to convert this to a hex unsigned char array!

std::string hex = "0c45a1bf"

unsigned char hexCh = ""
    [0] = "0c"
    [1] = "45"
    [2] = "a1"
    [3] = "bf"

I want this bevavior shown in hexCh!

Best way over stringstream and std::hex? Have you an implementation?!

Thx

leon22
  • 5,280
  • 19
  • 62
  • 100

5 Answers5

4

Assuming you want the values of each pair of the hex string:

std::string hex = "0c45a1bf";

int main(int argc, char **argv)
{
    union U
    {
        unsigned int value;
        unsigned char components[4];
    };

    U u;

    std::stringstream SS(hex);
    SS >> std::hex >> u.value;

    std::cout << u.components[0] << '\n'; // the 0c value
    std::cout << u.components[1] << '\n'; // the 45 value
    std::cout << u.components[2] << '\n'; // the a1 value
    std::cout << u.components[3] << '\n'; // the bf value
}

You can read the value into an union and get each sub-part.

PaperBirdMaster
  • 12,806
  • 9
  • 48
  • 94
  • 1
    This is undefined behavior, and what actually happens will depend on the system, architecture, etc. – James Kanze Nov 21 '12 at 10:42
  • Yes, i know that the answer isn't complete: i'm assuming that the hex string will always fit into `unsigned int` and i'm not thinking about alignments but... the question doesn't specify platform nor what hex format will be used, i just think that the use of an `union` is worth to know as a possible solution in some scope. – PaperBirdMaster Nov 21 '12 at 10:46
  • Reading from a member other than the last member written is undefined behavior. More significantly, in this case, you're ignoring the problem of byte order, as well as the size of an `int`. The size issue can easily be fixed by using `uint32_t`, instead of `int`. The byte order issue, on the other hand, has no simple solution. – James Kanze Nov 21 '12 at 11:07
  • @JamesKanze yes, using a fixed-size-type like `uint32_t` (where is it defined?, is from the standard?) could solve part of the problem but, the problem of undefined hex format and undefined lenght hex strings remains, could be a headache to process a string like `aa12345678`! – PaperBirdMaster Nov 21 '12 at 11:17
  • 1
    `uint32_t` is conditionally defined in ``, from C99 or C++11. (Most C++03 compilers will have it as well, because it is in C99.) It is only defined on machines which have a 32 bit integral type, so you won't find it on some exotic machines. (Unisys has, or at least had until recently, a 36 bit architecture with 9 bit bytes, and a 48 bit architecture with `sizeof(int) == 6`. But such machines have become truly exotic, and most people don't have to worry about them.) – James Kanze Nov 21 '12 at 11:29
4

Use std::stringstream + std::hex:

std::stringstream ss;
std::string hex = "0c45a1bf";
std::vector<unsigned char> hexCh;
unsigned int buffer;
int offset = 0;
while (offset < hex.length()) {
   ss.clear();
   ss << std::hex << hex.substr(offset, 2);
   ss >> buffer;
   hexCh.push_back(static_cast<unsigned char>(buffer));
   offset += 2;
}
Denis Ermolin
  • 5,530
  • 6
  • 27
  • 44
1

You can either convert the entire string into a larger integral type, and pick out the bytes from that. Something like:

std::vector<unsigned char>
asBytes( std::string const& input )
{
    std::istringstream parser( input );
    uint32_t tmp;
    input >> std::hex >> tmp;
    std::vector<unsigned char> results;
    //  The conversion implicitly does the & 0xFF
    results.push_back( tmp >> 24 );
    results.push_back( tmp >> 16 );
    results.push_back( tmp >>  8 );
    results.push_back( tmp       );
    return results;
}

Or, you could create substrings of two characters each, create an std::istringstream for each, and input from it. You'd still have to input to a type larger than char, because >> to a character type reads one character only, and assigns it. But you can read it into an int, then convert the int to unsigned char.

unsigned char
convertOneByte( std::string::const_iterator begin,
                std::string::const_iterator end )
{
    std::istringstream parser( std::string( begin, end ) );
    int tmp;
    parser >> std::hex >> tmp;
    return tmp;
}

std::vector<unsigned char>
asBytes( std::string const& input )
{
    std::vector<unsigned char> results;
    results.push_back( input.begin()    , input.begin() + 2 );
    results.push_back( input.begin() + 2, input.begin() + 4 );
    results.push_back( input.begin() + 4, input.begin() + 6 );
    results.push_back( input.begin() + 6, input.begin() + 8 );
    return results;
}

(Both bits of code need a lot more error checking. They're just to give you an idea.)

James Kanze
  • 150,581
  • 18
  • 184
  • 329
0

This 2 functions together shall do the job:

This one is straightforward:

inline int char2hex(char c)
{
   if (c >= '0' && c <= '9') return c - '0';
   if (c >= 'a' && c <= 'f') return c - 'a' + 10;
   if (c >= 'A' && c <= 'F') return c - 'A' + 10;
   throw std::runtime_error("wrong char");
}

This is little more complicated:

std::vector<unsigned char> str2hex(const std::string& hexStr)
{
     std::vector<unsigned char> retVal;
     bool highPart = ((hexStr.length() % 2) == 0);  
     // for odd number of characters - we add an extra 0 for the first one:
     if (!highPart)
         retVal.push_back(0);
     std::for_each(hexStr.begin(), hexStr.end(), 
        [&](char nextChar) {
           if (highPart) 
               // this is first char for the given hex number:
               retVal.push_back(0x10 * char2hex(nextChar));
           else
              // this is the second char for the given hex number
              retVal.back() += char2hex(nextChar);
           highPart = !highPart;
       }
     );

    return retVal;
}

And the example that it works:

int main() {
  std::string someHex =  "c45a1bf";
  std::vector<unsigned char> someUHex = str2hex(someHex);
  std::copy(someUHex.begin(), someUHex.end(), std::ostream_iterator<int>(std::cout << std::hex, ""));
}
PiotrNycz
  • 23,099
  • 7
  • 66
  • 112
0

A possible solution: (thx Denis Ermolin):

void ClassA::FuncA(unsigned char *answer)
{
    std::string hex = "0c45a1bf";
    std::stringstream convertStream;

    // if you have something like "0c 45 a1 bf" -> delete blanks
    hex.erase( std::remove(hex.begin(), hex.end(), ' '), hex.end() );

    int offset = 0, i = 0;      
    while (offset < hex.length()) 
    {
        unsigned int buffer;

        convertStream << std::hex << hex.substr(offset, 2);         
        convertStream >> std::hex >> buffer;

        answer[i] = static_cast<unsigned char>(buffer);

        offset += 2;
        i++;

        // empty the stringstream
        convertStream.str(std::string());
        convertStream.clear();
    }
}
leon22
  • 5,280
  • 19
  • 62
  • 100