Python / Numpy: extracción de bits de bytes

0

Tengo 8 bytes de datos en forma de numpy.frombuffer()matriz. Necesito obtener los bits 10-19 en una variable y los bits 20-29 en una variable. ¿Cómo uso Python para extraer bits que cruzan bytes? He leído sobre el cambio de bits, pero no tengo claro si esa es la forma de hacerlo.

9
  • ¿Podrías explicar tu pregunta con un ejemplo? 13 de oct a las 18:07
  • numpy no te ayudará aquí con la manipulación de bits (aunque es una gran biblioteca). es mejor quedarse con los bytesobjetos de Python (como el búfer que usó probablemente ya lo estaba). Vea mi respuesta para un ejemplo usando bytes(cadena de bytes)
    Aaron
    13 de oct a las 18:56
  • @ Aaron Yo desaconsejaría hacer comentarios tan fuertes a menos que uno esté absolutamente seguro de ello.
    Ehsan
    13 de oct a las 19:22
  • @Ehsan No creo que sea una declaración tan fuerte. Numpy puede ser capaz de hacer esto, pero no lo hará más fácil ni más comprensible. Particularmente para un programador nuevo, tiendo a recomendar que se ciña a la biblioteca estándar y las funciones integradas hasta que se sienta más cómodo con el lenguaje.
    Aaron
    13 de oct a las 19:26
  • 1
    Los datos son números enteros. Veré si puedo extraer datos de ejemplo.
    Kevin
    13 oct a las 19:31
0

Dependiendo de su tipo de datos, es posible que deba modificar ligeramente esta solución numpy:

a = np.frombuffer(b'\x01\x02\x03\x04\x05\x06\x07\x08', dtype=np.uint8)
#array([1, 2, 3, 4, 5, 6, 7, 8], dtype=uint8)

desembalaje de bits:

first = np.unpackbits(a)[10:20]
#array([0, 0, 0, 0, 1, 0, 0, 0, 0, 0], dtype=uint8)

Y si necesita volver a empaquetar las brocas:

first_packed = np.packbits(first)
array([8, 0], dtype=uint8)

Tenga en cuenta que Python es un índice basado en 0 y si desea el elemento 10 al 19, ajuste la indexación anterior a np.unpackbits(a)[9:19].

Del mismo modo para otro caso:

second = np.unpackbits(a)[20:30]
#array([0, 0, 1, 1, 0, 0, 0, 0, 0, 1], dtype=uint8)
2
  • gracias por esto. Es fácil y comprensible. Mis datos iniciales son 8 bytes. Necesito saber de la gerencia si todos los datos serán de 8 bytes.
    Kevin
    14 de oct a las 14:18
  • @Kevin de nada. Siéntase libre de editar su pregunta cuando tenga más información al respecto y comente aquí para que podamos revisarla.
    Ehsan
    14 oct a las 18:46
0

Obtenga cada bit individualmente indexando el byte correcto y luego enmascarando el bit correcto. Luego, puede cambiar y agregar para construir su nuevo número a partir de los bits.

data = b'abcdefgh' #8 bytes of data

def bit_slice(data, start, stop):
    out = 0
    for i in range(start, stop):
        byte_n = i//8
        byte_bit = i%8
        byte_mask = 1<<byte_bit
        bit = bool(data[byte_n] & byte_mask)
        out = out*2 + bit #multiply by 2 is equivalent to shift. Then add the new bit
    return out

re: comentarios

Cada vez que queremos agregar un nuevo bit a nuestro número así:

10110
101101

Tenemos que cambiar los primeros cinco bits y luego agregar 1 o 0 según el valor del siguiente bit. Si se desplaza hacia la izquierda, cada dígito se mueve un lugar más arriba, lo que en binario significa multiplicar por 2. En decimal, desplazar un número sobre un lugar significa multiplicar por 10. Cuando agregamos el nuevo bit a nuestro número que estamos acumulando, simplemente lo multiplico por 2. de usar el operador de turno correcto solo para mostrar que es otra opción. Al crear la máscara de bytes, utilicé el operador de desplazamiento derecho ( <<). Funciona cambiando un 1 en varios lugares, por lo que termino con un byte que tiene un 1 en el lugar correcto que cuando lo "y" con el byte en cuestión, obtengo solo el bit que quiero indexar:

1<<3 = 00001000
1<<5 = 00100000
1<<0 = 00000001
1<<7 = 10000000

luego aplica la máscara para obtener el bit que queremos:

10011011 #a byte of data
00100000 #bit mask for the 32's place
_________&
00000000
#bit in the 32's place is 0

10011011 #a byte of data
00010000 #bit mask for the 16's place
_________&
00010000
#bit in the 16's place is 1

Después de aplicar la máscara, si el bit seleccionado es 0, el número completo siempre será 0. Si el bit seleccionado es 1, el número siempre será mayor que 0. Invocar boolese resultado equivale a:

if data[byte_n] & byte_mask > 0:
    bit = 1
else:
    bit = 0

... porque un booleano interpretado como un entero es simplemente un 1 o un 0.

5
  • ¿Puede explicar "Multiplicar por 2 es equivalente a cambiar"?
    Kevin
    13 oct a las 19:16
  • Además, no obtengo el cálculo de byte_mask. ¿Puedes explicarme mas?
    Kevin
    13 oct a las 19:23
  • @Kevin ver editar
    Aaron
    13 oct a las 19:37
  • 1
    Gracias, @Aaron, por la explicación adicional. Esto tiene sentido ahora. Tu solución es con lo que estoy experimentando por ahora.
    Kevin
    14 de oct a las 14:07
  • La solución de @Kevin Ehsan también es correcta, y ciertamente es más corta, pero espero que la mía brinde un poco más de comprensión sobre cómo funciona bajo el capó ...
    Aaron
    14 de oct a las 15:58