【问题标题】:Read a large big-endian binary file读取大型大端二进制文件
【发布时间】:2016-11-03 09:19:20
【问题描述】:

我有一个非常大的大端二进制文件。我知道这个文件中有多少个数字。我找到了一个如何使用 struct 读取大端文件的解决方案,如果文件很小,它就可以完美工作:

    data = []
    file = open('some_file.dat', 'rb')

    for i in range(0, numcount)
            data.append(struct.unpack('>f', file.read(4))[0])

但如果文件大小超过 ~100 mb,则此代码运行速度非常慢。 我当前的文件大小为 1.5gb,包含 399.513.600 个浮点数。上面的代码使用这个文件大约需要 8 分钟。

我找到了另一个更快的解决方案:

    datafile = open('some_file.dat', 'rb').read()
    f_len = ">" + "f" * numcount   #numcount = 399513600

    numbers = struct.unpack(f_len, datafile)

这段代码运行大约需要 1.5 分钟,但这对我来说太慢了。早些时候,我在 Fortran 中编写了相同的功能代码,它在大约 10 秒内运行。

在 Fortran 中,我打开带有标志“big-endian”的文件,我可以简单地读取 REAL 数组中的文件而无需任何转换,但在 python 中,我必须将文件作为字符串读取,并使用 struct 每 4 位转换为浮点数。是否有可能使程序运行得更快?

【问题讨论】:

  • 我对@9​​87654323@也有一些不好的经历;一次读取约 1GB 的文件(您的第二个示例)完全耗尽了我笔记本电脑上的内存(8GB),这当然会使一切变得非常慢。分块阅读是我的解决方案。

标签: python file io


【解决方案1】:

可以使用numpy.fromfile读取文件,并在dtype参数中指定>指定类型为big-endian:

numpy.fromfile(filename, dtype='>f')

还有一个array.fromfile 方法,但不幸的是我看不到任何可以控制字节序的方法,因此根据您的用例,这可能会避免对第三方库的依赖或无用。

【讨论】:

    【解决方案2】:

    以下方法为我提供了很好的加速:

    import struct
    import random
    import time
    
    
    block_size = 4096
    start = time.time()
    
    with open('some_file.dat', 'rb') as f_input:    
        data = []
    
        while True:
            block = f_input.read(block_size * 4)
            data.extend(struct.unpack('>{}f'.format(len(block)/4), block))
    
            if len(block) < block_size * 4:
                break
    
    print "Time taken: {:.2f}".format(time.time() - start)
    print "Length", len(data)
    

    您可以指定计数而不是使用&gt;fffffff,例如&gt;1000f。它一次读取文件4096 块。如果读取的量小于这个值,它会调整块大小并退出。

    来自struct - Format Characters 文档:

    格式字符前面可以有一个整数重复计数。为了 例如,格式字符串“4h”的含义与“hhhh”完全相同。

    【讨论】:

    • 这种方法适用于相对“小”的文件(1-2 GB)。但是,鉴于问题的标题,您将如何以较小的块读取较大的文件(20-30 GB)而不会耗尽内存? (我知道它必须包含文件的某些分区才能工作)。
    【解决方案3】:
    def read_big_endian(filename):
        all_text = ""
        with open(filename, "rb") as template:
            try:
                template.read(2)  # first 2 bytes are FF FE
                while True:
                    dchar = template.read(2)
                    all_text += dchar[0]
            except:
                pass
        return all_text
    
    
    def save_big_endian(filename, text):
        with open(filename, "wb") as fic:
            fic.write(chr(255) + chr(254))  # first 2 bytes are FF FE
            for letter in text:
                fic.write(letter + chr(0))
    

    用于读取.rdp文件

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-12-19
      • 1970-01-01
      • 2014-04-27
      • 1970-01-01
      • 1970-01-01
      • 2011-04-18
      • 2016-09-20
      • 1970-01-01
      相关资源
      最近更新 更多