在位级读取二进制数据

时间:2017-10-20 12:17:47

标签: python python-3.x numpy

我有一个二进制文件,其中数据以16位整数块组织,如下所示:

  • 第15位:数字位1
  • 第14位:数字位2
  • 位13到0:14位有符号整数

我发现如何从文件中提取数据到3个数组的唯一方法是:

data = np.fromfile("test1.bin", dtype=np.uint16)

digbit1 = data >= 2**15

data = np.array([x - 2**15 if x >= 2**15 else x for x in data], dtype=np.uint16)

digbit2 = data >= 2**14

data = np.array([x-2**14 if x >= 2**14 else x for x in data])

data = np.array([x-2**14 if x >= 2**13 else x for x in data], dtype=np.int16)

现在我知道我可以使用for循环覆盖原始数据并填写3个单独的数组,但这仍然是丑陋的。我想知道的是如何以dtype=[('db', [('1', bit), ('2', bit)]), ('temp', 14bit-signed-int)])的方式更有效地执行此操作,以便像data['db']['1'] = array of ones and zeros一样轻松访问。

1 个答案:

答案 0 :(得分:2)

这是一种比你的代码更有效的方法,因为Numpy以编译速度进行循环,这比使用Python循环要快得多。我们可以使用按位算术代替那些if测试。

你没有提供任何样本数据,所以我写了一些简单的Python 3代码来创建一些假数据。我将这些数据以大endian格式保存到文件中,但如果您的数据实际存储在little-endian中,则很容易更改。我没有使用numpy.fromfile来读取数据,因为用普通的Python读取文件然后使用numpy.frombuffer转换读取的字节会更快。

唯一棘手的部分是处理那些14位有符号整数。我假设你正在使用two's complement代表。

import numpy as np

# Make some fake data
bdata = []
bitlen = 14
mask = (1 << bitlen) - 1
for i in range(12):
    # Two initial bits
    a = i % 4
    # A signed number
    b = i - 6
    # Combine initial bits with the signed number,
    # using 14 bit two's complement.
    n = (a << bitlen) | (b & mask)
    # Convert to bytes, using 16 bit big-endian
    nbytes = n.to_bytes(2, 'big')
    bdata.append(nbytes)
    print('{} {:2} {:016b} {} {:>5}'.format(a, b, n, nbytes.hex(), n))
print()

# Save the data to a file
fname = 'test1.bin'
with open(fname, 'wb') as f:
    f.write(b''.join(bdata))

# And read it back in
with open(fname, 'rb') as f:
    data = np.frombuffer(f.read(), dtype='>u2')

print(data)

# Get the leading bits
digbit1 = data >> 15
print(digbit1)

# Get the second bits
digbit2 = (data >> 14) & 1
print(digbit2)

# Get the 14 bit signed integers
data = ((data & mask) << 2).astype(np.int16) >> 2
print(data)

<强>输出

0 -6 0011111111111010 3ffa 16378
1 -5 0111111111111011 7ffb 32763
2 -4 1011111111111100 bffc 49148
3 -3 1111111111111101 fffd 65533
0 -2 0011111111111110 3ffe 16382
1 -1 0111111111111111 7fff 32767
2  0 1000000000000000 8000 32768
3  1 1100000000000001 c001 49153
0  2 0000000000000010 0002     2
1  3 0100000000000011 4003 16387
2  4 1000000000000100 8004 32772
3  5 1100000000000101 c005 49157

[16378 32763 49148 65533 16382 32767 32768 49153     2 16387 32772 49157]
[0 0 1 1 0 0 1 1 0 0 1 1]
[0 1 0 1 0 1 0 1 0 1 0 1]
[-6 -5 -4 -3 -2 -1  0  1  2  3  4  5]

如果您确实需要使用little-endian字节排序,只需在'<u2'调用中将dtype更改为np.frombuffer即可。并测试它,改变&#39;大&#39;小小的&#39;在假数据制作部分的n.to_bytes电话中。