If you are still having problems, as explained in the comments, unless you want to create a lookup-table (which is a fine way to go), your first task is to convert the ASCII characters "00.11.10.FF"
into actual numeric values of 0x0, 0x11, 0x10 & 0xff
. There are a number of ways to do this, but if you are reading with scanf
, you may as well let scanf
do it for you by reading with the "%x"
format specifier which will accept the hex-characters and perform the conversion.
For example, to read each of the quads into an array of unsigned
values named hex
, you could do:
#define QUAD 4u /* if you need a constant, #define one (or more) */
...
unsigned hex[QUAD] = {0}; /* let's input the values as numbers */
...
if (scanf ("%x.%x.%x.%x", &hex[0], &hex[1], &hex[2], &hex[3]) != 4) {
fputs ("error: invalid input.\n", stderr);
return 1;
}
(note: you could declare hex
as unsigned char
and save the additional bytes, but then you would want to limit the value read by scanf
using the hh
type-modifier, e.g. "%hhx"
, but that prefix isn't supported on older versions of VS, so in this case a simple unsigned
is used instead)
With the hex-characters converted to numeric values, all that remains is outputting the binary-representation of each (i.e. the value that is already stored in memory). To do so, you can simply loop and shift, checking whether the bit is a 0
or 1
and outputting the corresponding character '0'
or '1'
.
Additionally, since a simple loop and shift will only output until there are no more 1's
bits left, you will want to set the number of bits to display to make sure you get a full 8-per-byte by looping that number of times. A handy function to write the information to stdout
for the number of bits requested can be:
/** binary representation of 'v' padded to 'sz' bits.
* the padding amount is limited to the number of
* bits in 'v'. valid range: 1 - sizeof v * CHAR_BIT.
*/
void binprnpad (const unsigned long v, size_t sz)
{
if (!sz) { fprintf (stderr, "error: invalid sz.\n"); return; }
if (!v) { while (sz--) putchar ('0'); return; }
if (sz > sizeof v * CHAR_BIT)
sz = sizeof v * CHAR_BIT;
while (sz--)
putchar ((v >> sz & 1) ? '1' : '0');
}
(CHAR_BIT
is a macro representing the number of bits-per-byte and is found in limits.h
)
Putting it altogether, you could do:
#include <stdio.h>
#include <limits.h>
#define QUAD 4u /* if you need a constant, #define one (or more) */
#define QBITS 8u
/** binary representation of 'v' padded to 'sz' bits.
* the padding amount is limited to the number of
* bits in 'v'. valid range: 0 - sizeof v * CHAR_BIT.
*/
void binprnpad (const unsigned long v, size_t sz)
{
if (!sz) { fprintf (stderr, "error: invalid sz.\n"); return; }
if (!v) { while (sz--) putchar ('0'); return; }
if (sz > sizeof v * CHAR_BIT)
sz = sizeof v * CHAR_BIT;
while (sz--)
putchar ((v >> sz & 1) ? '1' : '0');
}
int main (void) {
unsigned hex[QUAD] = {0}; /* let's input the values as numbers */
fputs ("enter hex IP: ", stdout); /* read/validate input */
if (scanf ("%x.%x.%x.%x", &hex[0], &hex[1], &hex[2], &hex[3]) != 4) {
fputs ("error: invalid input.\n", stderr);
return 1;
}
for (unsigned i = 0; i < QUAD; i++) { /* loop over each quad */
if (i) /* if not zero output the '.' */
putchar ('.');
binprnpad (hex[i], QBITS); /* output the 8-bits per quad */
}
putchar ('\n'); /* tidy up with newline */
return 0;
}
Example Use/Output
$ ./bin/hexip2bin
enter hex IP: 00.11.10.FF
00000000.00010001.00010000.11111111
Reading Only Characters -- The Lookup Table Approach
If you can only read characters and cannot read into an array or string, then the simplest approach is to simply read a characters and lookup what its binary representation would be from a simply table that holds the binary representations for 0 - 15
(or [0-9A-F]
in other words).
The lookup table can be a simple global, e.g.
/* lookup table for hex byte binary representations */
char *lookup[] = { "0000", "0001", "0010", "0011",
"0100", "0101", "0110", "0111",
"1000", "1001", "1010", "1011",
"1100", "1101", "1110", "1111" };
The scheme is then straight forward, read a character from the user, if the character c
is 0-9
, just output lookup[c - '0']
(where c - '0'
results in the number 0-9
See: ASCIITable.com). If the character c
is [A-F]
then output lookup[c - '7']
(which subtracts the ASCII character value of '7'
to index 10-15
in the lookup table). Otherwise, if the character is a '.'
, just output it unchanged.
To protect against someone entering "00.11.10.ff"
, just convert all characters read to uppper-case with toupper()
provided in ctype.h
(or clear the 6th bit in all [a-f]
manually).
That's as simple as it gets. You don't even need scanf
, just getchar();
, e.g.
#include <stdio.h>
#include <ctype.h>
#define QBYTES 8u /* if you need a constant, #define one */
/* lookup table for hex byte binary representations */
char *lookup[] = { "0000", "0001", "0010", "0011",
"0100", "0101", "0110", "0111",
"1000", "1001", "1010", "1011",
"1100", "1101", "1110", "1111" };
int main (void) {
unsigned ndx = 0; /* index of the hex byte read */
fputs ("enter hex IP: ", stdout); /* read/validate input */
while (ndx < QBYTES) { /* read until 8 hex bytes read */
unsigned char c = toupper(getchar()); /* read char as upper-case */
if (isdigit (c)) { /* if [0-9] */
fputs (lookup[c - '0'], stdout);
ndx++; /* increment index */
}
else if (isxdigit (c)) { /* if [A-F] */
fputs (lookup[c - '7'], stdout);
ndx++;
}
else if (c == '.') /* if '.' */
putchar (c);
else { /* handle character not [0-9A-F.] */
fputs ("(error: invalid character input)\n", stderr);
break;
}
}
putchar ('\n'); /* tidy up with newline */
return 0;
}
Example Use/Output
$ ./bin/hexip2bin
enter hex IP: 00.11.10.FF
00000000.00010001.00010000.11111111
lower-case hex example:
$ ./bin/hexip2bin
enter hex IP: 00.11.10.ff
00000000.00010001.00010000.11111111
Read A char
Output A Nibble
And, of course, you can always mix and match between the methods. If you can't use a lookup-table, then just get the numeric value of the character and write the 4-bits representing that hex value as output, e.g.
#include <stdio.h>
#include <limits.h>
#include <ctype.h>
#define QUAD 4u /* if you need a constant, #define one (or more) */
#define QBYTES 8u
/** binary representation of 'v' padded to 'sz' bits.
* the padding amount is limited to the number of
* bits in 'v'. valid range: 1 - sizeof v * CHAR_BIT.
*/
void binprnpad (const unsigned long v, size_t sz)
{
if (!sz) { fprintf (stderr, "error: invalid sz.\n"); return; }
if (!v) { while (sz--) putchar ('0'); return; }
if (sz > sizeof v * CHAR_BIT)
sz = sizeof v * CHAR_BIT;
while (sz--)
putchar ((v >> sz & 1) ? '1' : '0');
}
int main (void) {
unsigned ndx = 0; /* index of the hex byte read */
fputs ("enter hex IP: ", stdout); /* read/validate input */
while (ndx < QBYTES) { /* read until 8 hex bytes read */
unsigned char c = toupper(getchar()); /* read char as upper-case */
if (isdigit (c)) { /* if [0-9] */
binprnpad (c - '0', QUAD);
ndx++;
}
else if (isxdigit (c)) { /* if [A-F] */
binprnpad (c - '7', QUAD);
ndx++;
}
else if (c == '.') /* if '.' */
putchar (c);
else { /* handle character not [0-9A-F.] */
fputs ("(error: invalid character input)\n", stderr);
break;
}
}
putchar ('\n'); /* tidy up with newline */
return 0;
}
(output is the same)
Just another way of skinning the same cat. Look things over and let me know if you have further questions.