8

I'm trying to convert a hex string to binary. I'm using:

echo "ibase=16; obase=2; $line" | BC_LINE_LENGTH=9999 bc

It is truncating the leading zeroes. That is, if the hex string is 4F, it is converted to 1001111 and if it is 0F, it is converted to 1111. I need it to be 01001111 and 00001111

What can I do?

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
gp_xps
  • 639
  • 1
  • 8
  • 12
  • Do you want your answer to be pure `bc`? There are a number of other languages you can do this at the command-line to convert a hex string to binary format with leading zeros. – Yzmir Ramirez Sep 28 '12 at 05:11

7 Answers7

6

The output from bc is correct; it simply isn't what you had in mind (but it is what the designers of bc had in mind). If you converted hex 4F to decimal, you would not expect to get 079 out of it, would you? Why should you get leading zeroes if the output base is binary? Short answer: you shouldn't, so bc doesn't emit them.

If you must make the binary output a multiple of 8 bits, you can add an appropriate number of leading zeroes using some other tool, such as awk:

awk '{ len = (8 - length % 8) % 8; printf "%.*s%s\n", len, "00000000", $0}'
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • I didn't think the output was incorrect. I just didn't want them cut off. Thanks! – gp_xps Sep 28 '12 at 05:23
  • 1
    Binary and hex are frequently used to represent bytes, and in this context I think it does make sense to expect leading zeros. Decimal, on the other hand, is not used to represent bytes. So I do think it's reasonable to want `4F` to convert to `01001111` in binary and `79` in decimal. – tytk Dec 12 '22 at 05:30
3

You can pipe to awk like this:

echo "ibase=16; obase=2; $line" | BC_LINE_LENGTH=9999 bc | awk '{ printf "%08d\n", $0 }' 
Steve
  • 51,466
  • 13
  • 89
  • 103
  • I am not sure this works for hex numbers that are bigger than a byte. The Python solution does work for that case. – Xofo Sep 28 '15 at 23:13
3

Pure Bash solution (beside bc):

paddy()
{
    how_many_bits=$1
    read number
    zeros=$(( $how_many_bits - ${#number} ))
    for ((i=0;i<$zeros;i++)); do
    echo -en 0
    done && echo $number
}

Usage:

>bc <<< "obase=2;ibase=16; 20" | paddy 8
00100000
Ralf
  • 16,086
  • 4
  • 44
  • 68
user173209
  • 31
  • 1
1

You can do it in python:

line=4F
python -c "print ''.join([bin(int(i, 16))[2:].zfill(4) for i in '$line'])"

result:

'01001111'
kev
  • 155,172
  • 47
  • 273
  • 272
  • If I had a stream if hex numbers, how would your PIPE the hex to this? I have a solution that works, but would like to see yours. – Xofo Sep 28 '15 at 23:48
  • I think this should be the accepted answer because (1) It does the binary conversion and (2) It does the zero formatting. – Xofo Sep 28 '15 at 23:49
0

What's frustrating is the bc expects the input to be zero padded but doesn't provide a similar output option. Here's another alternative using sed:

sed 's_0_0000_g;    s_1_0001_g;    s_2_0010_g;    s_3_0011_g;
     s_4_0100_g;    s_5_0101_g;    s_6_0110_g;    s_7_0111_g;
     s_8_1000_g;    s_9_1001_g;    s_[aA]_1010_g; s_[bB]_1011_g;
     s_[cC]_1100_g; s_[dD]_1101_g; s_[eE]_1110_g; s_[fF]_1111_g;'
Stephen Niedzielski
  • 2,497
  • 1
  • 28
  • 34
0

You can use seq and sed to help you pād:

function paddington(){
  PADDLE=8; while read IN; do
    seq -f '0' -s '' 1 $PADDLE | \
    sed "s/0\{${#IN}\}\$/$IN/"
  done
}

bc <<< "ibase=16; obase=2; 4F; 1E; 0F" | paddington

The output:

01001111
00011110
00001111
AXE Labs
  • 4,051
  • 4
  • 29
  • 29
0

You can use printf to left-pad the result with zeros if the result's length is not a multiple of four:

hex_nr=2C8B; hex_len=${#hex_nr}; binary_nr=$(bc <<< "obase=2;ibase=16;$hex_nr"); \
bin_length=$(( hex_len * 4 )); printf "%0${bin_length}d\n" $binary_nr

will result in

0010110010001011

instead of bc's output of

10110010001011

Matthias Braun
  • 32,039
  • 22
  • 142
  • 171