Каков наилучший способ манипулирования битовым полем в Python?

Я читаю какой-то протокол MPEG Transport Stream через UDP, и в нем есть несколько забавных битовых полей (например, длина 13). Я использую библиотеку «struct» для широкой распаковки, но есть ли простой способ сказать «Возьмите следующие 13 бит», вместо того, чтобы вручную настраивать манипуляции с битами? Я хотел бы что-то вроде того, как C делает битовые поля (без возврата к C).

Предложения?


person ZebZiggle    schedule 02.09.2008    source источник


Ответы (2)


Это часто задаваемый вопрос. Там есть запись ASPN Cookbook, которая служила мне в прошлом.

И есть обширная страница требований, которые один человек хотел бы видеть в модуле, делающем это.

person Thomas Vander Stichele    schedule 02.09.2008

Модуль bitstring предназначен именно для решения этой проблемы. Это позволит вам читать, изменять и создавать данные, используя биты в качестве основных строительных блоков. Последние версии предназначены для Python 2.6 или новее (включая Python 3), но версия 1.0 также поддерживает Python 2.4 и 2.5.

Подходящим примером для вас может быть следующий, который удаляет все пустые пакеты из транспортного потока (и вполне возможно использует ваше 13-битное поле?):

from bitstring import Bits, BitStream  

# Opening from a file means that it won't be all read into memory
s = Bits(filename='test.ts')
outfile = open('test_nonull.ts', 'wb')

# Cut the stream into 188 byte packets
for packet in s.cut(188*8):
    # Take a 13 bit slice and interpret as an unsigned integer
    PID = packet[11:24].uint
    # Write out the packet if the PID doesn't indicate a 'null' packet
    if PID != 8191:
        # The 'bytes' property converts back to a string.
        outfile.write(packet.bytes)

Вот еще один пример, включая чтение из битовых потоков:

# You can create from hex, binary, integers, strings, floats, files...
# This has a hex code followed by two 12 bit integers
s = BitStream('0x000001b3, uint:12=352, uint:12=288')
# Append some other bits
s += '0b11001, 0xff, int:5=-3'
# read back as 32 bits of hex, then two 12 bit unsigned integers
start_code, width, height = s.readlist('hex:32, 2*uint:12')
# Skip some bits then peek at next bit value
s.pos += 4
if s.peek(1):
    flags = s.read(9)

Вы можете использовать стандартную нотацию слайсов для нарезки, удаления, реверсирования, перезаписи и т. д. на уровне битов, а также есть функции поиска, замены, разделения и т. д. на уровне битов. Также поддерживаются различные порядок следования байтов.

# Replace every '1' bit by 3 bits
s.replace('0b1', '0b001')
# Find all occurrences of a bit sequence
bitposlist = list(s.findall('0b01000'))
# Reverse bits in place
s.reverse()

Полная документация находится здесь.

person Scott Griffiths    schedule 06.07.2009
comment
Я думаю, что пакет [11:24].uint должен быть пакетом [12:24].uint. Поле имеет длину 13 бит, начинается с 12-го бита и заканчивается 24-м битом. - person ; 17.07.2012
comment
Это действительно должен был быть комментарий, а не ответ, но нет, это действительно [11:24]. Индексы отсчитываются от нуля и не включают конечный индекс (что является стандартным использованием в Python и многих других языках). Таким образом, срез только первого бита будет [0:1], тогда как [12:24] будет 12-битным срезом с 13-го по 24-й бит включительно. Обратите внимание, что длина всегда является разницей между двумя индексами. - person Scott Griffiths; 02.08.2012