pythonbytebitstring

python bytes to bit string


I have value of the type bytes that need to be converted to BIT STRING

bytes_val = (b'\x80\x00', 14)

the bytes in index zero need to be converted to bit string of length as indicated by the second element (14 in this case) and formatted as groups of 8 bits like below.

expected output => '10000000 000000'B

Another example

bytes_val2 = (b'\xff\xff\xff\xff\xf0\x00', 45) #=> '11111111 11111111 11111111 11111111 11110000 00000'B

Solution

  • 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


    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__, funcs[0](b, n * 7) == 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