Another way to do this is by using the bitstring module:
>>> from bitstring import BitArray
>>> input_str = '0xff'
>>> c = BitArray(hex=input_str)
>>> c.bin
'0b11111111'
And if you need to strip the leading 0b:
>>> c.bin[2:]
'11111111'
The bitstring module isn't a requirement, as jcollado's answer shows, but it has lots of performant methods for turning input into bits and manipulating them. You might find this handy (or not), for example:
>>> c.uint
255
>>> c.invert()
>>> c.bin[2:]
'00000000'
etc.
Answer from Alex Reynolds on Stack OverflowAnother way to do this is by using the bitstring module:
>>> from bitstring import BitArray
>>> input_str = '0xff'
>>> c = BitArray(hex=input_str)
>>> c.bin
'0b11111111'
And if you need to strip the leading 0b:
>>> c.bin[2:]
'11111111'
The bitstring module isn't a requirement, as jcollado's answer shows, but it has lots of performant methods for turning input into bits and manipulating them. You might find this handy (or not), for example:
>>> c.uint
255
>>> c.invert()
>>> c.bin[2:]
'00000000'
etc.
What about something like this?
>>> bin(int('ff', base=16))
'0b11111111'
This will convert the hexadecimal string you have to an integer and that integer to a string in which each byte is set to 0/1 depending on the bit-value of the integer.
As pointed out by a comment, if you need to get rid of the 0b prefix, you can do it this way:
>>> bin(int('ff', base=16))[2:]
'11111111'
... or, if you are using Python 3.9 or newer:
>>> bin(int('ff', base=16)).removeprefix('0b')
'11111111'
Note: using lstrip("0b") here will lead to 0 integer being converted to an empty string. This is almost always not what you want to do.
Can Python work with bits?
python bytes to bit string - Stack Overflow
How to convert bits into bytes in python? - Stack Overflow
python - Construct: bytes and bits conversion - Code Review Stack Exchange
The simplest tactics to consume bits in 8-er chunks and ignore exceptions:
def getbytes(bits):
done = False
while not done:
byte = 0
for _ in range(0, 8):
try:
bit = next(bits)
except StopIteration:
bit = 0
done = True
byte = (byte << 1) | bit
yield byte
Usage:
lst = [1,0,0,0,0,0,0,0,1]
for b in getbytes(iter(lst)):
print b
getbytes is a generator and accepts a generator, that is, it works fine with large and potentially infinite streams.
Step 1: Add in buffer zeros
Step 2: Reverse bits since your endianness is reversed
Step 3: Concatenate into a single string
Step 4: Save off 8 bits at a time into an array
Step 5: ???
Step 6: Profit
def bitsToBytes(a):
a = [0] * (8 - len(a) % 8) + a # adding in extra 0 values to make a multiple of 8 bits
s = ''.join(str(x) for x in a)[::-1] # reverses and joins all bits
returnInts = []
for i in range(0,len(s),8):
returnInts.append(int(s[i:i+8],2)) # goes 8 bits at a time to save as ints
return returnInts
My problem is that whenever I want to work with bits, let's say I want to create an 8 bit flag, Python automatically converts them to Bytes. Plus it doesn't distinguish between them. If Ilen() 8 bits, I get 8. If I len() 8 bytes I get 8. If I len() a string with 8 characters I get 8. I don't really know how should i work with bits. I can do the flags with bytes, but that seems weird. I waste 7 bits. I tried to convert a number using the bin() function which worked, but when I encoded() or sent over the network it was converted into Bytes. So 8 bytes instead of 8 bits, which means I wasted 56 bits. Any ideas?
What about some combination of formatting (below with f-string but can be done otherwise), and slicing:
def bytes2binstr(b, n=None):
s = ' '.join(f'{x:08b}' for x in b)
return s if n is None else s[:n + n // 8 + (0 if n % 8 else -1)]
If I understood correctly (I am not sure what the B at the end is supposed to mean), it passes your tests and a couple more:
func = bytes2binstr
args = (
(b'\x80\x00', None),
(b'\x80\x00', 14),
(b'\x0f\x00', 14),
(b'\xff\xff\xff\xff\xf0\x00', 16),
(b'\xff\xff\xff\xff\xf0\x00', 22),
(b'\x0f\xff\xff\xff\xf0\x00', 45),
(b'\xff\xff\xff\xff\xf0\x00', 45),
)
for arg in args:
print(arg)
print(repr(func(*arg)))
# (b'\x80\x00', None)
# '10000000 00000000'
# (b'\x80\x00', 14)
# '10000000 000000'
# (b'\x0f\x00', 14)
# '00001111 000000'
# (b'\xff\xff\xff\xff\xf0\x00', 16)
# '11111111 11111111'
# (b'\xff\xff\xff\xff\xf0\x00', 22)
# '11111111 11111111 111111'
# (b'\x0f\xff\xff\xff\xf0\x00', 45)
# '00001111 11111111 11111111 11111111 11110000 00000'
# (b'\xff\xff\xff\xff\xf0\x00', 45)
# '11111111 11111111 11111111 11111111 11110000 00000'
Explanation
- we start from a
bytesobject - iterating through it gives us a single byte as a number
- each byte is 8 bit, so decoding that will already give us the correct separation
- each byte is formatted using the
bbinary specifier, with some additional formatting:0zero fill,8minimum length - we join (concatenate) the result of the formatting using
' 'as "separator" - finally the result is returned as is if a maximum number of bits
nwas not specified (set toNone), otherwise the result is cropped ton+ the number of spaces that were added in-between the 8-character groups.
In the solution above 8 is somewhat hard-coded.
If you want it to be a parameter, you may want to look into (possibly a variation of) @kederrac first answer using int.from_bytes().
This could look something like:
def bytes2binstr_frombytes(b, n=None, k=8):
s = '{x:0{m}b}'.format(m=len(b) * 8, x=int.from_bytes(b, byteorder='big'))[:n]
return ' '.join([s[i:i + k] for i in range(0, len(s), k)])
which gives the same output as above.
Speedwise, the int.from_bytes()-based solution is also faster:
for i in range(2, 7):
n = 10 ** i
print(n)
b = b''.join([random.randint(0, 2 ** 8 - 1).to_bytes(1, 'big') for _ in range(n)])
for func in funcs:
print(func.__name__, funcs0 == func(b, n * 7))
%timeit func(b, n * 7)
print()
# 100
# bytes2binstr True
# 10000 loops, best of 3: 33.9 ยตs per loop
# bytes2binstr_frombytes True
# 100000 loops, best of 3: 15.1 ยตs per loop
# 1000
# bytes2binstr True
# 1000 loops, best of 3: 332 ยตs per loop
# bytes2binstr_frombytes True
# 10000 loops, best of 3: 134 ยตs per loop
# 10000
# bytes2binstr True
# 100 loops, best of 3: 3.29 ms per loop
# bytes2binstr_frombytes True
# 1000 loops, best of 3: 1.33 ms per loop
# 100000
# bytes2binstr True
# 10 loops, best of 3: 37.7 ms per loop
# bytes2binstr_frombytes True
# 100 loops, best of 3: 16.7 ms per loop
# 1000000
# bytes2binstr True
# 1 loop, best of 3: 400 ms per loop
# bytes2binstr_frombytes True
# 10 loops, best of 3: 190 ms per loop
you can use:
def bytest_to_bit(by, n):
bi = "{:0{l}b}".format(int.from_bytes(by, byteorder='big'), l=len(by) * 8)[:n]
return ' '.join([bi[i:i + 8] for i in range(0, len(bi), 8)])
bytest_to_bit(b'\xff\xff\xff\xff\xf0\x00', 45)
output:
'11111111 11111111 11111111 11111111 11110000 00000'
steps:
transform your bytes to an integer using int.from_bytes
str.formatmethod can take a binary format spec.
also, you can use a more compact form where each byte is formatted:
def bytest_to_bit(by, n):
bi = ' '.join(map('{:08b}'.format, by))
return bi[:n + len(by) - 1].rstrip()
bytest_to_bit(b'\xff\xff\xff\xff\xf0\x00', 45)
You can convert the string back to an integer with int() passing a base of 2 and then back to a character with chr():
temp = format(ord('a'), 'b')
print(temp)
#'1100001'
c = chr(int(temp, 2))
print(c)
# 'a'
Mark Meyer's answer is spot on, and works for any character:
>>> char = '๐'
>>> bits = format(ord(char), 'b')
>>> bits
'11111011000001110'
>>> char = chr(int(bits, 2))
>>> char
'๐'
But it only works for characters, not for grapheme clusters. Suppose you had the woman scientist emoji:
>>> char = '๐ฉโ๐ฌ'
>>> bits = format(ord(char), 'b')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: ord() expected a character, but string of length 3 found
This does not work because the woman scientist emoji is not a single character, but rather a grapheme cluster made up of three characters:
- WOMAN
- ZERO WIDTH JOINER
- MICROSCOPE
So the string has three characters and you can not do ord on a three character string.
I think it's important to not here that turning a single character into a bit string for its code point is highly unusual and in practice this is never really done (unless you are using an encoding known as UTF-32 BE, in which case you should pad the bit string out with zeros to 32 places). IMHO, what you should be doing here is NOT using ord and chr, but rather encoding and decoding using UTF-8. The very idea of turning characters into bits or bytes should be done with a well known character encoding scheme, and UTF-8 is the most proper.
Here is how I would suggest you do the character and bit thing:
>>> char = '๐ฉโ๐ฌ'
>>> bytes = char.encode('utf-8')
>>> bytes
b'\xf0\x9f\x91\xa9\xe2\x80\x8d\xf0\x9f\x94\xac'
>>> char = bytes.decode('utf-8')
>>> char
'๐ฉโ๐ฌ'
If you want bits and not bytes, then:
>>> char = '๐ฉโ๐ฌ'
>>> bytes = char.encode('utf-8')
>>> bits = ''.join(f'{b:08b}' for b in bytes)
>>> bits
'1111000010011111100100011010100111100010100000001000110111110000100111111001010010101100'
Read the bits from a file, low bits first.
def bits(f):
bytes = (ord(b) for b in f.read())
for b in bytes:
for i in xrange(8):
yield (b >> i) & 1
for b in bits(open('binary-file.bin', 'r')):
print b
The smallest unit you'll be able to work with is a byte. To work at the bit level you need to use bitwise operators.
x = 3
#Check if the 1st bit is set:
x&1 != 0
#Returns True
#Check if the 2nd bit is set:
x&2 != 0
#Returns True
#Check if the 3rd bit is set:
x&4 != 0
#Returns False