位,字节,字
位、字节、字是计算机数据存储的基本单位,每一个位存储一个1位的二进制编码(即0或1),一个字节由八位组成。字是代表计算机处理指令或数据的二进制位数,32位系统1字=32位,64位系统1字=64位
bitstring 库
bitstring是一个python的库,可以使得对二进制数据的操作变得简单。
下载
https://bitstring.readthedocs.io/en/latest/
创建bitArray
a = bitArray('0xff91')
b = bitArray('0b110')
0x是十六进制前缀,0b是二进制前缀。文档中说其未成bytes会内部处理。若输入的不是字符串会创造输入整数位的空比特串。
使用内部转换
a.bin
'1111111100000001'
b.oct
'6'
b.int
-2
a.bytes
b'\xff\x01'
这些属性是在执行时开始计算,bin以及oct返回的是字符串。并且需要oct原比特串满足是3位的倍数。而hex需要是4位。bytes属性可以返回python3的bytes对象。
比特串填充
(b + [0]).hex
'c'
([0] + b).hex
'6'
零位[0]也可以写成BitArray([0]), BitArray([0]), BitArray('0b0'), BitArray(bin='0'),'0b0' 可以是1
修改比特串
bitArray可以将其视为位列表,使用索引可以对其进行切片,删除,插入。
删除
print(a[3:9])
0b111110
del a[-6:]
print(a)
0b1111111100
这里的del是删除列表中的a[-1]~a[-6]对象,解除了对原数据的引用。不过我想既然引用计数已经为0,数据占用的空间应该也被释放了。
切片
单个的元素会返回一个bool值
添加
a.prepend('0b01')
a.append('0o7')
a += '0x06'
查找
a = BitArray('0xa9f')
a.find('0x4f')
(3,)
a == '0b101, 0x4f, 0b1'
True
构造比特串
sequence_header() | No. of bits | Mnemonic |
---|---|---|
sequence_header_code | 32 | bslbf |
horizontal_size_value | 12 | uimsbf |
vertical_size_value | 12 | uimsbf |
aspect_ratio_information | 4 | uimsbf |
frame_rate_code | 4 | uimsbf |
bit_rate_value | 18 | uimsbf |
marker_bit | 1 | bslbf |
vbv_buffer_size_value | 10 | uimsbf |
constrained_parameters_flag | 1 | bslbf |
load_intra_quantiser_matrix | 1 | uimsbf |
if (load_intra_quantiser_matrix) | ||
{ intra_quantiser_matrix[64] } | 8*64 | uimsbf |
load_non_intra_quantiser_matrix | 1 | uimsbf |
if (load_non_intra_quantiser_matrix) | ||
{ non_intra_quantiser_matrix[64] } | 8*64 | uimsbf |
next_start_code() |
s = BitArray()
s.append('0x000001b3') # the sequence_header_code
s.append('uint:12=352') # 12 bit unsigned integer
s.append('uint:12=288')
...
# s = BitArray('0x000001b3, uint:12=352, uint:12=288')
'''
width, height = 352, 288
s = bitstring.pack('0x000001b3, 2*uint:12', width, height)
'''
其中pack函数可以用字典作为第二个参数
fmt = 'sequence_header_code,
uint:12=horizontal_size_value,
uint:12=vertical_size_value,
uint:4=aspect_ratio_information,
...
'
d = {'sequence_header_code': '0x000001b3',
'horizontal_size_value': 352,
'vertical_size_value': 288,
'aspect_ratio_information': 1,
...
}
s = bitstring.pack(fmt, **d)
pack 返回的是bit流
操作如下:
>>> s
BitStream('0x000001b31601201')
>>> s.pos
0
>>> s.read(24)
BitStream('0x000001')
>>> s.pos
24
>>> s.read('hex:8')
'b3'
>>> s.pos
32
>>> s.readlist('2*uint:12')
[352, 288]
>>> s.unpack('bytes:4, 2*uint:12, uint:4')
['\x00\x00\x01\xb3', 352, 288, 1]
bitStream的操作有类似游标的位移,unpack是从比特串开头开始。
创建
bitstrings库一共提供了四个类,Bits,BitArray,BitStream,ConstBitStream。
使用类的情况
- 如果要改变比特串的内容,使用bitArray或者bitSteam
- 如果要作为字典中的key使用,那么使用bits或者constBitSteam
- 如果直接从文件中读取比特流,bitArray 或者bitSteam会将整个文件读入内存,而bits或者constBitStream不会。
- 内存效率最高的类是bits。
从原始字节数据创建
a = BitArray(bytes=b'\x00\x01\x02\xff', length=28, offset=1)
b = BitArray(bytes=open("somefile", 'rb').read())
lenth的单位是bits,offset同理偏移量截断开头。
包装
创建bitStream对象可以用pack函数,
bitstring.pack(format, *values, **kwargs)
例如:
s = bitstring.pack('hex:32, uint:12, uint:12', '0x000001b3', 352, 288)
# 相当于
s = BitStream('0x0000001b3, uint:12=352, uint:12=288')
这个可以用函数实现更通用的创建
def foo(a, b, c, d):
return bitstring.pack('uint:8, 0b110, int:6, bin, bits', a, b, c, d)
s1 = foo(12, 5, '0b00000', '')
s2 = foo(101, 3, '0b11011', s1)
切片,索引,拼接。
切片
>>> a = BitArray('0b00011110')
>>> b = a[3:7]
>>> print(a, b)
0x1e 0xf
索引
单个位索引返回一个布尔值,len属性返比特串长度
拼接
- append和prepend
- join拼接比特串
- del删除
- insert在bitarray使用时需要指定pos,bitstream会在当前pos进行insert。
- overwrite和insert大致相同,但是会覆盖。
使用函数 | 使用切片 |
---|---|
s.insert(bs, pos) | s[pos:pos] = bs |
s.overwrite(bs, pos) | s[pos:pos + bs.len] = bs |
s.append(bs) | s[s.len:s.len] = bs |
s.prepend(bs) | s[0:0] = bs` |
分割
>>> a = BitArray('0x4700004711472222')
>>> for s in a.split('0x47', bytealigned=True):
... print(s.hex)
470000
4711
472222
spilit返回迭代器
>>> a = BitArray('0x47001243')
>>> for byte in a.cut(8):
... print(byte.hex)
47
00
12
43
cut可以分成相等的部分。
读取和解析
每一个比特流都有一个pos属性,长度范围为0到最后一位。
>>> s = ConstBitStream(filename='test/test.m1v')
>>> print(s.pos)
0
>>> start_code = s.read(32).hex
>>> width = s.read(12).uint
>>> height = s.read(12).uint
>>> print(start_code, width, height, s.pos)
000001b3 352 288 56
>>> s.pos += 37
>>> flags = s.read(2)
>>> constrained_parameters_flag = flags.read(1)
>>> load_intra_quantiser_matrix = flags.read(1)
>>> print(s.pos, flags.pos)
95 2
如果一次读取多个数据可以用readlist
a, b, c = s.readlist([32, 8, 24])
peek方法不会改变bitstream的pos属性。其使用和read类似。
查找等操作和字符串类似不在列举
杂项
- bytealign()方法返回跳跃步数,使得pos成为8的倍数
- reverse将bitarray反转,可以指定范围
- tobytes会把bitarray转换为字节,如果不够8位在后面补0
- startswith / endswith判断比特串是否已某开头或结束
文章评论