Как читать биты из файла?

Я знаю, как читать байты - x.read(number_of_bytes), но как я могу читать биты в Python?

Мне нужно прочитать только 5 бит (не 8 бит [1 байт]) из двоичного файла

Любые идеи или подход?

Ответ 1

Python может читать только байты за раз. Вам нужно будет прочитать полный байт, а затем просто извлечь нужное значение из этого байта, например.

b = x.read(1)
firstfivebits = b >> 3

Или, если вам нужны 5 наименее значимых бит, а не 5 наиболее значимых бит:

b = x.read(1)
lastfivebits = b & 0b11111

Некоторые другие полезные сведения о манипуляциях с битами можно найти здесь: http://wiki.python.org/moin/BitManipulation

Ответ 2

Как говорится в принятом ответе, стандартный ввод/вывод Python может считывать и записывать только целые байты за раз. Однако вы можете смоделировать такой поток битов, используя этот рецепт для побитового ввода-вывода.

Обновления

После изменения версии Rosetta Code Python для работы в неизменном виде как в Python 2 & 3, я включил эти изменения в этот ответ.

В дополнение к этому позже, после того, как меня вдохновил комментарий, сделанный @mhernandez, я внес дополнительные изменения в код Rosetta, чтобы он поддерживал так называемый протокол менеджера контекста, который позволяет экземплярам обоих его двух классов быть используется в инструкциях Python with. Последняя версия показана ниже:

class BitWriter(object):
    def __init__(self, f):
        self.accumulator = 0
        self.bcount = 0
        self.out = f

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.flush()

    def __del__(self):
        try:
            self.flush()
        except ValueError:   # I/O operation on closed file.
            pass

    def _writebit(self, bit):
        if self.bcount == 8:
            self.flush()
        if bit > 0:
            self.accumulator |= 1 << 7-self.bcount
        self.bcount += 1

    def writebits(self, bits, n):
        while n > 0:
            self._writebit(bits & 1 << n-1)
            n -= 1

    def flush(self):
        self.out.write(bytearray([self.accumulator]))
        self.accumulator = 0
        self.bcount = 0


class BitReader(object):
    def __init__(self, f):
        self.input = f
        self.accumulator = 0
        self.bcount = 0
        self.read = 0

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        pass

    def _readbit(self):
        if not self.bcount:
            a = self.input.read(1)
            if a:
                self.accumulator = ord(a)
            self.bcount = 8
            self.read = len(a)
        rv = (self.accumulator & (1 << self.bcount-1)) >> self.bcount-1
        self.bcount -= 1
        return rv

    def readbits(self, n):
        v = 0
        while n > 0:
            v = (v << 1) | self._readbit()
            n -= 1
        return v

if __name__ == '__main__':
    import os
    import sys
    # Determine this module name from it file name and import it.
    module_name = os.path.splitext(os.path.basename(__file__))[0]
    bitio = __import__(module_name)

    with open('bitio_test.dat', 'wb') as outfile:
        with bitio.BitWriter(outfile) as writer:
            chars = '12345abcde'
            for ch in chars:
                writer.writebits(ord(ch), 7)

    with open('bitio_test.dat', 'rb') as infile:
        with bitio.BitReader(infile) as reader:
            chars = []
            while True:
                x = reader.readbits(7)
                if not reader.read:  # End-of-file?
                    break
                chars.append(chr(x))
            print(''.join(chars))

Другой пример использования, показывающий, как "сжать" 8-битный поток ASCII-байтов, отбрасывая наиболее значимый "неиспользуемый" бит... и прочитать его обратно (однако ни один из них не использует его как менеджер контекста).

import sys
import bitio

o = bitio.BitWriter(sys.stdout)
c = sys.stdin.read(1)
while len(c) > 0:
    o.writebits(ord(c), 7)
    c = sys.stdin.read(1)
o.flush()

... и "оpipeить" тот же поток:

import sys
import bitio

r = bitio.BitReader(sys.stdin)
while True:
    x = r.readbits(7)
    if not r.read:  # nothing read
        break
    sys.stdout.write(chr(x))

Ответ 3

Это появляется в верхней части поиска Google для чтения битов с использованием Python.

Я нашел bitstring хорошим пакетом для чтения битов, а также улучшением по сравнению с собственными возможностями (что неплохо для Python 3.6), например.

# import module
from bitstring import ConstBitStream

# read file
b = ConstBitStream(filename='file.bin')

# read 5 bits
output = b.read(5)

# convert to unsigned int
integer_value = output.uint

Больше документации и подробностей здесь:https://pythonhosted.org/bitstring/index.html