【发布时间】:2020-06-10 05:54:09
【问题描述】:
我知道如何读取字节 — x.read(number_of_bytes),但是如何在 Python 中读取位?
我只需要从二进制文件中读取 5 位(而不是 8 位 [1 字节])
有什么想法或方法吗?
【问题讨论】:
-
这些位是连续的吗?如果是这样,字节中的五个最高有效位或五个最低有效位?
我知道如何读取字节 — x.read(number_of_bytes),但是如何在 Python 中读取位?
我只需要从二进制文件中读取 5 位(而不是 8 位 [1 字节])
有什么想法或方法吗?
【问题讨论】:
Python 一次只能读取一个字节。你需要读入一个完整的字节,然后从那个字节中提取你想要的值,例如
b = x.read(1)
firstfivebits = b >> 3
或者,如果您想要 5 个最低有效位,而不是 5 个最高有效位:
b = x.read(1)
lastfivebits = b & 0b11111
其他一些有用的位操作信息可以在这里找到:http://wiki.python.org/moin/BitManipulation
【讨论】:
firstfivebits = b >> 3 你确定你应该对比特感到厌烦吗? (你可能会失明什么的;)。
正如公认的答案所述,标准 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))
【讨论】:
bitio 类,使其支持context manager protocol,就像内置的file 类所做的那样,这可能是一项非常值得的努力——而且是一种更好的照顾方式的问题。
这出现在使用 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
【讨论】: