0

I need to covert hexadecimal string to binary then pass the bits into different variables.

For example, my input is: std::string hex = "E136";

How do I convert the string into binary output 1110 0001 0011 0110? After that I need to pass the bit 0 to variable A, bits 1-9 to variable B and bits 10-15 to variable C.

Thanks in advance

Sun
  • 13
  • 3
  • 3
    You are expected to show your attempt to solve this, instead of just asking us to write the code for you. Apart from anything else seeing how you code will give us a good idea of your level of skill, and so help make a better answer for you. – john Aug 25 '18 at 07:54
  • The separation of bits can be done using bit operators (applied to the integral value - conversion back to text not needed in this case). There are lots of resources to learn - google "c++ bit operators" and you will find e.g. this one: [C++ Bitwise Operators on Tutorials Point](https://www.tutorialspoint.com/cplusplus/cpp_bitwise_operators.htm) – Scheff's Cat Aug 25 '18 at 08:12

2 Answers2

1

How do I convert the string [...]?

Start with result value of null, then for each character (starting at first, indicating most significant one) determine its value (in range of [0:15]), multiply the so far received result by 16 and add the current value to. For your given example, this will result in

(((0 * 16 + v('E')) * 16 + v('1')) * 16 + v('3')) + v('6')

There are standard library functions doing the stuff for you, such as std::strtoul:

char* end;
unsigned long value = strtoul(hex.c_str(), &end, 16);
//                                               ^^ base!

The end pointer useful to check if you have read the entire string:

if(*char == 0)
{
    // end of string reached
}
else
{
    // some part of the string was left, you might consider this
    // as error (could occur if e. g. "f10s12" was passed, then
    // end would point to the 's') 
}

If you don't care for end checking, you can just pass nullptr instead.

Don't convert back to a string afterwards, you can get the required values by masking (&) and bitshifting (>>), e. g getting bits [1-9]:

uint32_t b = value >> 1 & 0x1ffU;

Working on integrals is much more efficient than working on strings. Only when you want to print out the final result, then convert back to string (if using a std::ostream, operator<< already does the work for you...).

Aconcagua
  • 24,880
  • 4
  • 34
  • 59
  • There is nothing wrong in your answer but... Isn't this a bit too abstract for an entry level programmer? What's wrong with `unsigned value = strtoul(hex.c_str(), nullptr, 16);`? – Scheff's Cat Aug 25 '18 at 08:10
  • @Scheff Ah, you are right, came to not providing code as I initially assumed OP wanted to do the stuff manually (as so often asked...) and I did not want to releave her/him from writing the loop her/himself... – Aconcagua Aug 25 '18 at 08:18
0

While playing with this sample, I realized that I gave a wrong recommendation:

std::setbase(2) does not work by standard. Ouch! (SO: Why doesn't std::setbase(2) switch to binary output?)

For conversion of numbers to string with binary digits, something else must be used. I made this small sample. Though, the separation of bits is considered as well, my main focus was on output with different bases (and IMHO worth another answer):

#include <algorithm>
#include <iomanip>
#include <iostream>
#include <string>

std::string bits(unsigned value, unsigned w)
{
  std::string text;
  for (unsigned i = 0; i < w || value; ++i) {
    text += '0' + (value & 1); // bit -> character '0' or '1'
    value >>= 1; // shift right one bit
  }
  // text is right to left -> must be reversed
  std::reverse(text.begin(), text.end());
  // done
  return text;
}

void print(const char *name, unsigned value)
{
  std::cout
    << name << ": "
    // decimal output
    << std::setbase(10) << std::setw(5) << value
    << " = "
    // binary output
#if 0 // OLD, WRONG:
  // std::setbase(2) is not supported by standard - Ouch!
    << "0b" << std::setw(16) << std::setfill('0') << std::setbase(2) << value
#else // NEW:
    << "0b" << bits(value, 16)
#endif // 0
    << " = "
    // hexadecimal output
    << "0x" << std::setw(4) << std::setfill('0') << std::setbase(16) << value
    << '\n';
}

int main()
{
  std::string hex = "E136";
  unsigned value = strtoul(hex.c_str(), nullptr, 16);
  print("hex", value);
  // bit 0 -> a
  unsigned a = value & 0x0001;
  // bit 1 ... 9 -> b
  unsigned b = (value & 0x03FE) >> 1;
  // bit 10 ... 15 -> c
  unsigned c = (value & 0xFC00) >> 10;
  // report
  print(" a ", a);
  print(" b ", b);
  print(" c ", c);
  // done
  return 0;
}

Output:

hex: 57654 = 0b1110000100110110 = 0xe136
 a : 00000 = 0b0000000000000000 = 0x0000
 b : 00155 = 0b0000000010011011 = 0x009b
 c : 00056 = 0b0000000000111000 = 0x0038

Live Demo on coliru

Concerning, the bit operations:

  • binary bitwise and operator (&) is used to set all unintended bits to 0. The second value can be understood as mask. It would be more obvious if I had used binary numbers but this is not supported in C++. Hex codes do nearly as well as a hex digit represents always the same pattern of 4 bits. (as 16 = 24) After some time of practice, you usually learn to "see" the bits in the hex code.

  • About the right shift (>>), I was not quite sure. OP didn't require that bits have to be moved somewhere – only that they had to be separated into distinct variables. So, these right-shift's might be obsolete.

So, this question which seemed to be trivial leaded to a surprising enlightment (for me).

Scheff's Cat
  • 19,528
  • 6
  • 28
  • 56
  • Thanks Scheff. You are a great help :) – Sun Aug 25 '18 at 09:02
  • Actually, my hex is "E1367B0180A70C00" which means strtoul() can't work. I guess i need to break the hex into two variables then use strtoul(). – Sun Aug 25 '18 at 10:13
  • @Sun Actually, this isn't a problem really. As I already mentioned, 1 hex digit makes always the same 4 binary digits (aka bits). So, you may process your string in portions of 4 (or even 8) characters (aka 4 or 8 hex digits). This could even be done in a loop. (Actually, the number of hex digits which can be stored in an `unsigned` can be coded as expression: `sizeof (unsigned) * 2` as 2 hex digits can exactly represent 1 byte.) – Scheff's Cat Aug 25 '18 at 10:19
  • @Sun And btw. `"E1367B0180A70C00"` are 16 digits -> can be stored with 8 bytes (e.g. as `std::uint64_t`). If you use [`strtoull()`](https://en.cppreference.com/w/cpp/string/byte/strtoul). Chances are good that `unsigned long long` provides at least 64 bits. (To ensure that this is true on your platform, you could write a `static_assert(sizeof(unsigned long long) >= sizeof (std::uint64_t));`. In the unexpected case that your platform/compiler doesn't support this you would get a compiler error. – Scheff's Cat Aug 25 '18 at 10:27