Prevent bc from auto truncating leading zeros when converting from hex to binary
Asked Answered
D

7

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?

Delinquent answered 28/9, 2012 at 4:19 Comment(1)
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.Disbelieve
D
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}'
Dysphemia answered 28/9, 2012 at 4:57 Comment(2)
I didn't think the output was incorrect. I just didn't want them cut off. Thanks!Delinquent
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.Oraliaoralie
S
4

You can pipe to awk like this:

echo "ibase=16; obase=2; $line" | BC_LINE_LENGTH=9999 bc | awk '{ printf "%08d\n", $0 }' 
Shalon answered 28/9, 2012 at 5:16 Comment(1)
I am not sure this works for hex numbers that are bigger than a byte. The Python solution does work for that case.Nipple
L
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
Lisabethlisan answered 25/8, 2020 at 9:32 Comment(0)
S
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'
Snooker answered 28/9, 2012 at 4:36 Comment(2)
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.Nipple
I think this should be the accepted answer because (1) It does the binary conversion and (2) It does the zero formatting.Nipple
M
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;'
Mills answered 2/2, 2013 at 22:27 Comment(0)
V
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
Vani answered 21/9, 2016 at 23:34 Comment(0)
I
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

Innominate answered 11/8, 2020 at 17:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.