# Print a string in binary format

Bengt Richter bokr at oz.net
Sat Jan 22 05:11:32 CET 2005

```On Sat, 22 Jan 2005 00:45:19 +1000, Nick Coghlan <ncoghlan at iinet.net.au> wrote:

>neutrino wrote:
>> Greetings to the Python gurus,
>>
>> I have a binary file and wish to see the "raw" content of it. So I open
>> it in binary mode, and read one byte at a time to a variable, which
>> will be of the string type. Now the problem is how to print the binary
>> format of that charater to the standard output. It seems a common task
>> but I just cannot find the appropriate method from the documentation.
>> Thanks a lot.
>>
>
>FWIW, I work with serial data a lot, and I find the following list comprehension
>to be a handy output tool for binary data:
>
>   print " ".join(["%0.2X" % ord(c) for c in data])
>
>The space between each byte helps keep things from degenerating into a
>meaningless mass of numbers, and using 2-digit hex instead of binary works
>towards the same purpose. (I actually currently use the hex() builtin, but the
>above just occurred to me, and it will give nicer formatting, and avoids the
>C-style "0x" prefixing each byte)
>
>Here's an interesting twiddle, though (there's probably already something along
>these lines in the cookbook):
Looks like you also played with this problem, after Alex posted a request for alternative
one-liner solutions to a question on an Italian newsgroup last October? ("show_base" reminded me
of "number_in_base")

BTW, my final version was (which can be put on one line ;-)

def number_in_base(x, N=10, digits='0123456789ABCDEF'):
return '-'[:x<0]+''.join([digits[r] for q in [abs(x)]
for q,r in iter(lambda:divmod(q, N), (0,0))][::-1]) or digits[0]

(it also takes care of sign and lets you encode with alternative digits if you like ;-)

>
>Py> def show_base(val, base, min_length = 1):
>...   chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
>...   if base < 2: raise ValueError("2 is minimum meaningful base")
>...   if base > len(chars): raise ValueError("Not enough characters for base")
>...   new_val = []
>...   while val:
>...     val, remainder = divmod(val, base)
>...     new_val.append(chars[remainder])
>...   result = "".join(reversed(new_val))
>...   return ("0" * (min_length - len(result))) + result
Hm, learn something every day ;-)
It didn't occur to me that a string multiplied by a negative number would default
nicely to the same result as multiplying by zero.

>...
>Py> show_base(10, 2)
>'1010'
>Py> show_base(10, 2, 8)
>'00001010'
>Py> show_base(10, 16, 2)
>'0A'
>Py> show_base(254, 16, 2)
>'FE'
>Py> show_base(0, 16)
>'0'
>Py> for base in range(2, 36):
>...   for testval in range(1000):
>...     assert testval == int(show_base(testval, base), base)
>...
>Py>
I guess that's a good idea (supplying full set of alphanumeric digits)

>>> def number_in_base(x, N=10, digits='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'):
...    return '-'[:x<0]+''.join([digits[r] for q in [abs(x)]
...            for q,r in iter(lambda:divmod(q, N), (0,0))][::-1]) or digits[0]
...
>>> for base in range(2, 36):
...     for testval in range(1000):
...         assert testval == int(number_in_base(testval, base), base)
...
>>>

For that matter, might as well go for Z base and negatives too:

>>> for base in range(2, 37):
...     for testval in range(-500, 500):
...         assert testval == int(number_in_base(testval, base), base)
...
>>>
(assert did not complain)

>>> for base in range(2, 37):
...     for testval in (-base+2, -base+1, -base, base-2, base-1,base):
...         print '%4s:%-4s'%(testval, number_in_base(testval, base)),
...     print
...
0:0      -1:-1     -2:-10     0:0       1:1       2:10
-1:-1     -2:-2     -3:-10     1:1       2:2       3:10
-2:-2     -3:-3     -4:-10     2:2       3:3       4:10
-3:-3     -4:-4     -5:-10     3:3       4:4       5:10
-4:-4     -5:-5     -6:-10     4:4       5:5       6:10
-5:-5     -6:-6     -7:-10     5:5       6:6       7:10
-6:-6     -7:-7     -8:-10     6:6       7:7       8:10
-7:-7     -8:-8     -9:-10     7:7       8:8       9:10
-8:-8     -9:-9    -10:-10     8:8       9:9      10:10
-9:-9    -10:-A    -11:-10     9:9      10:A      11:10
-10:-A    -11:-B    -12:-10    10:A      11:B      12:10
-11:-B    -12:-C    -13:-10    11:B      12:C      13:10
-12:-C    -13:-D    -14:-10    12:C      13:D      14:10
-13:-D    -14:-E    -15:-10    13:D      14:E      15:10
-14:-E    -15:-F    -16:-10    14:E      15:F      16:10
-15:-F    -16:-G    -17:-10    15:F      16:G      17:10
-16:-G    -17:-H    -18:-10    16:G      17:H      18:10
-17:-H    -18:-I    -19:-10    17:H      18:I      19:10
-18:-I    -19:-J    -20:-10    18:I      19:J      20:10
-19:-J    -20:-K    -21:-10    19:J      20:K      21:10
-20:-K    -21:-L    -22:-10    20:K      21:L      22:10
-21:-L    -22:-M    -23:-10    21:L      22:M      23:10
-22:-M    -23:-N    -24:-10    22:M      23:N      24:10
-23:-N    -24:-O    -25:-10    23:N      24:O      25:10
-24:-O    -25:-P    -26:-10    24:O      25:P      26:10
-25:-P    -26:-Q    -27:-10    25:P      26:Q      27:10
-26:-Q    -27:-R    -28:-10    26:Q      27:R      28:10
-27:-R    -28:-S    -29:-10    27:R      28:S      29:10
-28:-S    -29:-T    -30:-10    28:S      29:T      30:10
-29:-T    -30:-U    -31:-10    29:T      30:U      31:10
-30:-U    -31:-V    -32:-10    30:U      31:V      32:10
-31:-V    -32:-W    -33:-10    31:V      32:W      33:10
-32:-W    -33:-X    -34:-10    32:W      33:X      34:10
-33:-X    -34:-Y    -35:-10    33:X      34:Y      35:10
-34:-Y    -35:-Z    -36:-10    34:Y      35:Z      36:10
>>>

Of course, this is the prefixed-sign and absolute value representation,
which is no good if you are using base 2, 8, or 16 to get an idea of
underlying bits in a negative two-s complement representation.
But that's another thread ;-)

Regards,
Bengt Richter

```