当前位置: 首页 > 知识库问答 >
问题:

在Python中从二进制文件提取特定字节

姬实
2023-03-14

我有非常大的二进制文件,其中包含y传感器的x个int16数据点,以及包含一些基本信息的头文件。二进制文件被写为每个采样时间的y值,最多x个采样,然后是另一组读数,依此类推。如果我想要所有的数据,我使用的是numpy。fromfile(),它工作得又快又好。然而,如果我只需要传感器数据的子集或特定传感器,我目前有一个可怕的double for循环,使用的是文件。seek(),文件。read(),和结构。unpack()这要花很长时间。在python中有没有其他更快的方法?也许是我不太理解的mmap?或者只是使用整个fromfile(),然后再进行二次采样?

data = numpy.empty(num_pts, sensor_indices)
for i in range(num_pts):
    for j in range(sensor_indices):
        curr_file.seek(bin_offsets[j])
        data_binary = curr_file.read(2)
        data[j][i] = struct.unpack('h', data_binary)[0]

遵循了@rrauenza关于mmap的建议,这是一个很好的信息,我将代码编辑为

mm = mmap.mmap(curr_file.fileno(), 0, access=mmap.ACCESS_READ)
data = numpy.empty(num_pts,sensor_indices)
for i in range(num_pts):
    for j in range(len(sensor_indices)):
        offset += bin_offsets[j] * 2
        data[j][i] = struct.unpack('h', mm[offset:offset+2])[0]

虽然这比以前快了,但它仍然比以前慢了几个数量级

shape = (x, y)
data = np.fromfile(file=self.curr_file, dtype=np.int16).reshape(shape)
data = data.transpose()
data = data[sensor_indices, :]
data = data[:, range(num_pts)]

我用一个较小的30 Mb文件进行了测试,该文件只有16个传感器,30秒的数据。原始代码为160秒,mmap为105秒,np。fromfile和subsampling为0.33秒。

剩下的问题是-清楚地使用numpy。fromfile()对于小文件来说更好,但是对于大得多的文件来说,会有问题吗?这些文件可能高达20 Gb,并且有数小时或数天的数据和多达500个传感器?


共有1个答案

史烨
2023-03-14

我肯定会尝试mmap()

https://docs.python.org/2/library/mmap.html

如果您为每个int16调用search()read(),则会读取很多小位,这些小位会产生大量系统调用开销。你正在提取。

我编写了一个小测试来演示:

#!/usr/bin/python

import mmap
import os
import struct
import sys

FILE = "/opt/tmp/random"  # dd if=/dev/random of=/tmp/random bs=1024k count=1024
SIZE = os.stat(FILE).st_size
BYTES = 2
SKIP = 10


def byfile():
    sum = 0
    with open(FILE, "r") as fd:
        for offset in range(0, SIZE/BYTES, SKIP*BYTES):
            fd.seek(offset)
            data = fd.read(BYTES)
            sum += struct.unpack('h', data)[0]
    return sum


def bymmap():
    sum = 0
    with open(FILE, "r") as fd:
        mm = mmap.mmap(fd.fileno(), 0, prot=mmap.PROT_READ)
        for offset in range(0, SIZE/BYTES, SKIP*BYTES):
            data = mm[offset:offset+BYTES]
            sum += struct.unpack('h', data)[0]
    return sum


if sys.argv[1] == 'mmap':
    print bymmap()

if sys.argv[1] == 'file':
    print byfile()

我运行每个方法两次以补偿缓存。我使用time因为我想测量usersys时间。

以下是结果:

[centos7:/tmp]$ time ./test.py file
-211990391

real    0m44.656s
user    0m35.978s
sys     0m8.697s
[centos7:/tmp]$ time ./test.py file
-211990391

real    0m43.091s
user    0m37.571s
sys     0m5.539s
[centos7:/tmp]$ time ./test.py mmap
-211990391

real    0m16.712s
user    0m15.495s
sys     0m1.227s
[centos7:/tmp]$ time ./test.py mmap
-211990391

real    0m16.942s
user    0m15.846s
sys     0m1.104s
[centos7:/tmp]$ 

(sum-211990391只是验证两个版本做同样的事情。)

查看每个版本的第2个结果,mmap()是实时的~1/3。用户时间是~1/2,系统时间是~1/5。

您还可以选择以下方式来加快速度:

(1)正如您所提到的,加载整个文件。大I/O而不是小I/O可以加快速度。但是,如果您超过系统内存,您将退回到分页,这将比mmap()更糟糕(因为您必须分页)。我在这里不是很有希望,因为mmap已经在使用更大的I/O。

(2) 并发性。也许通过多个线程并行读取文件可以加快速度,但您将需要处理Python GIL。通过避免GIL,多处理将更好地工作,并且您可以轻松地将数据传递回顶级处理程序。然而,这将对下一个项目——位置——起作用:您可能会使I/O更加随机。

(3) 地点。以某种方式组织数据(或对读取进行html" target="_blank">排序),以便数据更紧密地联系在一起mmap()根据系统页面大小分块分页文件:

>>> import mmap
>>> mmap.PAGESIZE
4096
>>> mmap.ALLOCATIONGRANULARITY
4096
>>> 

如果您的数据距离较近(在4k区块内),那么它将已经加载到缓冲区缓存中。

(4) 更好的硬件。就像SSD一样。

我在SSD上运行了这个,速度快得多。我运行了python的概要文件,想知道解包是否很昂贵。它不是:

$ python -m cProfile test.py mmap                                                                                                                        
121679286
         26843553 function calls in 8.369 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    6.204    6.204    8.357    8.357 test.py:24(bymmap)
        1    0.012    0.012    8.369    8.369 test.py:3(<module>)
 26843546    1.700    0.000    1.700    0.000 {_struct.unpack}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
        1    0.000    0.000    0.000    0.000 {method 'fileno' of 'file' objects}
        1    0.000    0.000    0.000    0.000 {open}
        1    0.000    0.000    0.000    0.000 {posix.stat}
        1    0.453    0.453    0.453    0.453 {range}

增编:

好奇心占了我的上风,我尝试了多重处理。我需要仔细查看我的分区,但是在不同的试验中解包的数量(53687092)是相同的:

$ time ./test2.py 4
[(4415068.0, 13421773), (-145566705.0, 13421773), (14296671.0, 13421773), (109804332.0, 13421773)]
(-17050634.0, 53687092)

real    0m5.629s
user    0m17.756s
sys     0m0.066s
$ time ./test2.py 1
[(264140374.0, 53687092)]
(264140374.0, 53687092)

real    0m13.246s
user    0m13.175s
sys     0m0.060s

代码:

#!/usr/bin/python

import functools
import multiprocessing
import mmap
import os
import struct
import sys

FILE = "/tmp/random"  # dd if=/dev/random of=/tmp/random bs=1024k count=1024
SIZE = os.stat(FILE).st_size
BYTES = 2
SKIP = 10


def bymmap(poolsize, n):
    partition = SIZE/poolsize
    initial = n * partition
    end = initial + partition
    sum = 0.0
    unpacks = 0
    with open(FILE, "r") as fd:
        mm = mmap.mmap(fd.fileno(), 0, prot=mmap.PROT_READ)
        for offset in xrange(initial, end, SKIP*BYTES):
            data = mm[offset:offset+BYTES]
            sum += struct.unpack('h', data)[0]
            unpacks += 1
    return (sum, unpacks)


poolsize = int(sys.argv[1])
pool = multiprocessing.Pool(poolsize)
results = pool.map(functools.partial(bymmap, poolsize), range(0, poolsize))
print results
print reduce(lambda x, y: (x[0] + y[0], x[1] + y[1]), results)
 类似资料:
  • 我有一个很大的二进制文件。我想从中提取某些字符串并将它们复制到一个新的文本文件中。 例如,在: 我想取数字“7”(在之后),以及它之后的每个字符都停在(忽略)。 不幸的是,我对grep、sed等的知识并没有扩展到这个层次。有人能提出一个可行的方法来达到这个目的吗? 将显示所有带有“7”后跟字母的字符串,但这并不多。 谢谢你。 我想检查一个字符串的结尾,如果它以结尾,则grep后面的另一个字符串(中

  • 根据https://docs.python.org/3.4/library/venv.html#module-venv所说的“每个虚拟环境都有自己的Python二进制文件(允许创建具有各种Python版本的环境)”,那么我如何使用具有Python 2.7二进制文件的venv模块来创建虚拟环境呢?

  • 问题内容: 我正在尝试在Python中读取BMP文件。我知道前两个字节表示BMP公司。接下来的4个字节是文件大小。当我执行时: 我得到: ValueError:以10为底的int()的无效文字:’F#\ x13’ 我想做的是将这四个字节读取为整数,但是Python似乎将它们读取为字符并返回一个字符串,该字符串无法转换为整数。如何正确执行此操作? 问题答案: 该方法将字节序列作为字符串返回。要将字符

  • 问题内容: 我发现用Python读取二进制文件特别困难。你能帮我个忙吗?我需要读取此文件,在Fortran 90中,该文件很容易被读取 详细而言,文件格式为: 如何使用Python阅读?我尝试了一切,但没有成功。我是否有可能在python中使用f90程序,读取此二进制文件,然后保存需要使用的数据? 问题答案: 读取二进制文件内容,如下所示: 然后使用struct.unpack “解压缩”二进制数据

  • 问题内容: 我正在尝试从URLConnection读取二进制文件。当我使用文本文件对其进行测试时,它似乎可以正常工作,但对于二进制文件则不能。发送文件时,我在服务器上使用以下mime类型: 但是到目前为止,似乎没有任何效果。这是我用来接收文件的代码: 问题答案: 我就是这样

  • 问题内容: 我不是在谈论特定的行号,因为我正在读取具有相同格式但长度不同的多个文件。 说我有这个文本文件: 我希望你知道我的意思。我正在考虑遍历文件,然后使用正则表达式搜索以找到“开始”和“结束”的行号,然后使用线缓存从开始行读取到结束行。但是如何获得行号?我可以使用什么功能? 问题答案: 如果您只想要和之间的文本块,则可以执行以下操作: 实际上,您不需要操纵行号即可读取开始和结束标记之间的数据。