【问题标题】:How to read bits from a file?如何从文件中读取位?
【发布时间】:2020-06-10 05:54:09
【问题描述】:

我知道如何读取字节 — x.read(number_of_bytes),但是如何在 Python 中读取位?

我只需要从二进制文件中读取 5 位(而不是 8 位 [1 字节])

有什么想法或方法吗?

【问题讨论】:

  • 这些位是连续的吗?如果是这样,字节中的五个最高有效位或五个最低有效位?

标签: python io binary


【解决方案1】:

Python 一次只能读取一个字节。你需要读入一个完整的字节,然后从那个字节中提取你想要的值,例如

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

或者,如果您想要 5 个最低有效位,而不是 5 个最高有效位:

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

其他一些有用的位操作信息可以在这里找到:http://wiki.python.org/moin/BitManipulation

【讨论】:

  • 当我的声望增长到15时,我会给你竖起大拇指的! (我是新来的)所以,如果我这样做: b = x.read(1) firstfivebits = b >> 3 我会得到前 5 位......为什么不是 firstfivebits = b >> 5?你的意思是......为什么 b >> 3?
  • @HugoMedina 如果你不知道为什么firstfivebits = b >> 3 你确定你应该对比特感到厌烦吗? (你可能会失明什么的;)。
  • 现在我明白了,因为 1 字节 = 8 位,我们将应用右移运算符 3(例如删除这 3 个最低有效位),因此我们将获得字节中剩余的 5 位
【解决方案2】:

正如公认的答案所述,标准 Python I/O 一次只能读取和写入整个字节。但是,您可以使用Bitwise I/O 的这个配方来模拟这样的比特流。

更新

在修改 Rosetta Code 的 Python 版本以在 Python 2 和 3 中保持不变后,我将这些更改合并到此答案中。

除此之外,后来,在受到@mhernandez 评论的启发后,我进一步修改了 Rosetta 代码,使其支持所谓的context manager protocol,它允许在 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's name from it's 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()

...并“消除”相同的流:

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))

【讨论】:

  • +1 表示自包含的 sn-p。请注意, main 可能无法阅读其含义,因为当阅读器尝试阅读时可能不会删除作者。调用 writer.flush() 即可解决。
  • @mhernandez:扩展bitio 类,使其支持context manager protocol,就像内置的file 类所做的那样,这可能是一项非常值得的努力——而且是一种更好的照顾方式的问题。
  • 同意,事实上这正是我所做的。谢谢楼主
  • mhernandez:很高兴听到它有帮助。顺便说一句,我最近修改了 Rosetta Code 的 Python 版本,因此它也支持上下文管理器协议,然后在此处相应地更新了我的答案。 (按此顺序完成是因为 Rosetta Code 的许可允许在这样的上下文中逐字复制。)
【解决方案3】:

这出现在使用 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

【讨论】:

  • 我同意位串很有帮助。当您需要一次读取超过 8 位时,您需要了解这些位是如何“分散”在字节上的。例如。我需要读入一个 14 位整数。这就是我成功的方式: buf1 = b.read(8); buf2 = b.read(2); buf3 = b.read(6); str_with_bits = str(buf3.bin) + str(buf1.bin); int_value = int(str_with_bits, 2);
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-03-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-03-20
  • 2015-04-29
相关资源
最近更新 更多