【问题标题】:Read specific sections of a binary file containing 32-bit floats读取包含 32 位浮点数的二进制文件的特定部分
【发布时间】:2018-11-21 18:58:41
【问题描述】:

我有一个包含 32 位浮点数的二进制文件。我需要能够将文件的某些部分读入list 或其他类似数组的结构。换句话说,我需要一次读取特定数量的字节(特定数量的float32s)到我的数据结构中,然后使用seek() 寻找文件中的另一个点并再次执行相同的操作。

在伪代码中:

new_list = []

with open('my_file.data', 'rb') as file_in:
    for idx, offset in enumerate(offset_values):
        # seek in the file by the offset
        # read n float32 values into new_list[idx][:]

最有效/最不容易混淆的方法是什么?

【问题讨论】:

  • 使用 numpy.memmap 将文件作为 dtype numpy.float32 的 numpy 数组进行内存映射。

标签: python python-3.x file-io


【解决方案1】:

您可以使用 struct 模块将字节与 32 位 float 值相互转换:

import random
import struct

FLOAT_SIZE = 4
NUM_OFFSETS = 5
filename = 'my_file.data'

# Create some random offsets.
offset_values = [i*FLOAT_SIZE for i in range(NUM_OFFSETS)]
random.shuffle(offset_values)

# Create a test file
with open(filename, 'wb') as file:
    for offset in offset_values:
        file.seek(offset)
        value = random.random()
        print('writing value:', value, 'at offset', offset)
        file.write(struct.pack('f', value))

# Read sections of file back at offset locations.

new_list = []
with open(filename, 'rb') as file:
    for offset in offset_values:
        file.seek(offset)
        buf = file.read(FLOAT_SIZE)
        value = struct.unpack('f', buf)[0]
        print('read value:', value, 'at offset', offset)
        new_list.append(value)

print('new_list =', new_list)

样本输出:

writing value: 0.0687244786128608 at offset 8
writing value: 0.34336034914481284 at offset 16
writing value: 0.03658244351244533 at offset 4
writing value: 0.9733690320097427 at offset 12
writing value: 0.31991994765615206 at offset 0
read value: 0.06872447580099106 at offset 8
read value: 0.3433603346347809 at offset 16
read value: 0.03658244386315346 at offset 4
read value: 0.9733690023422241 at offset 12
read value: 0.3199199438095093 at offset 0
new_list = [0.06872447580099106, 0.3433603346347809, 0.03658244386315346,
            0.9733690023422241, 0.3199199438095093]

请注意,读回的值略有不同,因为 Python 在内部使用 64 位 float 值,因此在将它们转换为 32 位然后再返回的过程中会丢失一些精度。

【讨论】:

  • 这看起来很有希望。如果我需要一次读取多个浮点数(即一整行值到列表的一行中)怎么办?我会使用包含struct.unpack('f', buf)[0]for 循环来运行struct.unpack 操作,其次数与我需要的值一样多吗?
  • @questionable_code:是的,您可以在for 循环中执行此操作,但使用struct.unpack() 函数执行此操作会更有效,因为它每次都能够解压缩多个值如果你给它正确的格式字符串(即其中四个为'4f'),它就会被调用。请注意,严格来说,二进制文件中没有“行”,因此要在 seek() 到组的开头之后以这种方式使用它,您需要将所需数量的 FLOAT_SIZE 字节读入buf 缓冲区。
  • 如果我需要的值的数量是可变的怎么办?我将如何为此编写格式字符串?
  • @questionable_code:如果您知道每个偏移量预期的 32 位浮点数,则可以轻松构建所需的格式字符串。
【解决方案2】:

输入文件中的二进制信息可以很容易地映射到虚拟内存using mmap. 如果需要,您可以从那里将缓冲区导入到 numpy 数组中。请注意 - numpy dtype 可能会根据您的 32 位浮点数是有符号还是无符号而改变(此示例假定有符号)。填充的数组将包含数字(而不是原始字节)。

import mmap
import numpy as np
import os

new_list = []

with open('my_file.data', 'rb') as file_in:
    size_bytes = os.fstat(file_in.fileno()).st_size
    m = mmap.mmap(file_in.fileno(), length=size_bytes, access=mmap.ACCESS_READ)
    arr = np.frombuffer(m, np.dtype('float32'), offset=0)
    for idx, offset in enumerate(offset_values):
        new_list.append(arr[offset//4])  #For unsigned 32bit floats, divide by 8

我用一个 n=10000 的随机浮点数组对此进行了测试,转换为字节:

import random
import struct

a = ''
for i in range(10000):
    a += struct.pack('<f', random.uniform(0, 1000))

然后我将这个“a”变量读入 numpy 数组,就像你从文件中读取二进制信息一样。

>>> arr = np.frombuffer(a, np.dtype('float32'), offset=0)
>>> arr[500]
634.24408

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-12-22
    • 2011-09-11
    • 1970-01-01
    • 1970-01-01
    • 2015-11-11
    • 1970-01-01
    • 2011-07-26
    • 1970-01-01
    相关资源
    最近更新 更多