如何在 C++ 或 Python 中快速将二进制文件划分为 6 字节块? [关闭]

Posted

技术标签:

【中文标题】如何在 C++ 或 Python 中快速将二进制文件划分为 6 字节块? [关闭]【英文标题】:How to divide a binary file to 6-byte blocks in C++ or Python with fast speed? [closed] 【发布时间】:2019-05-11 17:22:42 【问题描述】:

我正在将 C++ 和 Python 中的文件作为二进制文件读取。我需要将二进制文件分成块,每块 6 个字节。例如,如果我的文件是 600 字节,结果应该是 100 块,每块 6 字节。

我已经尝试过结构(在 C++ 和 Python 中)和数组(Python)。它们都没有将二进制文件分成 6 个字节的块。他们只能将二进制分成每个 2 的幂(1、2、4、8、16 等)的块。

数组算法非常快,可以在不到一秒的时间内以 4 字节块的形式读取 1 GB 的二进制数据。相比之下,我用了一些其他的方法,但是都非常慢,几十分钟就搞定了几兆。

如何尽可能快地将二进制文件读取为 6 个字节的块? C++ 或 Python 中的任何帮助都会很棒。谢谢。

编辑 - 代码:

    struct Block

    char data[6];
;

class BinaryData

private:
    char data[6];

public:
    BinaryData() ;
    ~BinaryData() ;

    void readBinaryFile(string strFile)
    
        Block block;
        ifstream binaryFile;
        int size = 0;

        binaryFile.open(strFile, ios::out | ios::binary);
        binaryFile.seekg(0, ios::end);
        size = (int)binaryFile.tellg();
        binaryFile.seekg(0, ios::beg);
        cout << size << endl;
        while ( (int)binaryFile.tellg() < size )
        
            cout << binaryFile.tellg() << " , " << size << " , " << 
size - (int)binaryFile.tellg() << endl;
            binaryFile.read((char*)block.data,sizeof(block.data));
            cout << block.data << endl;
            //cin  >> block.data;
            if (size - (int)binaryFile.tellg() > size)
            
                break;
            
        
        binaryFile.close();

    

;

注意事项:

文件中的数字采用大端序 (remark) 目标是尽可能快地阅读它们,然后按升序对它们进行排序 (remark)

【问题讨论】:

你试过读入unsigned char[6]吗? 在不知道自己真正想要实现的目标的情况下,很难给出合适的解决方案。如果您通过data = open(filename, "rb").read()(或类似方式)读取文件,您将获得一个字节串,并且使用[data[i:i+n] for i in range(0, len(data), n)] 之类的内容,您可以获得一个拆分列表,或者您可以通过data[i*width:(i+1)*width] 直接寻址每个块。但我的直觉告诉我,你试图以一种非常不合常规的方式解决问题。尤其是由于 python 不喜欢迭代任务,即如果执行不当会非常慢。 @RichardCritten 我已将代码作为图像附加到帖子中。 @ChristianB.我想将二进制分成6字节的块,然后比较它们的值并按升序排序。 @Behzad 数据应该是什么,一个 6 字节的数字?如果是的话,小/大端呢? cout &lt;&lt; block.data &lt;&lt; endl 令人惊讶 【参考方案1】:

让我们从简单开始,然后进行优化。

简单循环

uint8_t  array1[6];
while (my_file.read((char *) &array1[0], 6))

    Process_Block(&array1[0]);

上面的代码读入一个文件,每次 6 个字节,然后将块发送给一个函数。 符合要求,不是很理想。

读取更大的块

文件是流媒体设备。他们有开始流式传输的开销,但保持流式传输非常有效。换句话说,我们希望每个事务读取尽可能多的数据以减少开销。

static const unsigned int CAPACITY = 6 * 1024;
uint8_t block1[CAPACITY];
while (my_file.read((char *) &block1[0], CAPACITY))

    const size_t bytes_read = my_file.gcount();
    const size_t blocks_read = bytes_read / 6;
    uint8_t const * block_pointer = &block1[0];
    while (blocks_read > 0)
    
        Process_Block(block_pointer);
        block_pointer += 6;
        --blocks_read;
    

上述代码在一个事务中最多读取 1024 个块。读取后,将每个块发送到一个函数进行处理。

此版本比简单循环更有效,因为它在每个事务中读取更多数据。 调整 CAPACITY 以在您的平台上找到最佳尺寸。

循环展开

前面的代码减少了输入传输速度的第一个瓶颈(尽管仍有优化的空间)。另一种技术是通过在循环内执行更多数据处理来减少处理循环的开销。这称为循环展开

const size_t bytes_read = my_file.gcount();
const size_t blocks_read = bytes_read / 6;
uint8_t const * block_pointer = &block1[0];
while ((blocks_read / 4) != 0)

    Process_Block(block_pointer);
    block_pointer += 6;

    Process_Block(block_pointer);
    block_pointer += 6;

    Process_Block(block_pointer);
    block_pointer += 6;

    Process_Block(block_pointer);
    block_pointer += 6;
    blocks_read -= 4;

while (blocks_read > 0)

    Process_Block(block_pointer);
    block_pointer += 6;
    --blocks_read;

您可以调整循环中的操作数量,看看它如何影响程序的速度。

多线程和多个缓冲区

另外两种加快数据读取速度的技术是使用多线程和多缓冲区。

一个线程,一个输入线程,将文件读入缓冲区。读入第一个缓冲区后,线程设置一个信号量,指示有数据要处理。输入线程读入下一个缓冲区。如此重复,直到数据全部被读取。 (对于一个挑战,弄清楚如何重用缓冲区并通知其他线程哪些缓冲区可用)。

第二个线程是处理线程。该处理线程首先启动并等待第一个缓冲区被完全读取。缓冲区有数据后,处理线程开始处理数据。处理完第一个缓冲区后,处理线程从下一个缓冲区开始。重复此过程,直到处理完所有缓冲区。

这里的目标是使用尽可能多的缓冲区来保持处理线程运行而不是等待。

编辑 1:其他技术

内存映射文件

一些操作系统支持内存映射文件。操作系统将文件的一部分读入内存。当访问内存之外的位置时,操作系统会将另一部分加载到内存中。需要测量(分析)这种技术是否提高了性能。

并行处理和线程

添加多个线程可能会显示微不足道的性能增益。计算机有一条数据总线(数据高速公路)连接许多硬件设备,包括内存、文件 I/O 和处理器。设备将被暂停以让其他设备使用数据高速公路。对于多个内核或处理器,一个处理器可能需要等待,而另一个处理器正在使用数据高速公路。当使用多线程或并行处理时,这种等待可能会导致可忽略的性能增益。此外,操作系统在构建和维护线程时也会产生开销。

【讨论】:

【参考方案2】:

尝试一下,输入文件是在程序的参数中接收的,正如你所说,我假设文件中的 6 字节值是按大端顺序写入的,但我不假设程序读取文件然后排序,它可以在小端和大端上执行(我在执行时检查案例)

#include <iostream>
#include <fstream>
#include <vector>
#include <cstdint>
#include <algorithm>
#include <limits.h> // CHAR_BIT

using namespace std;

#if CHAR_BIT != 8
# error that code supposes a char has 8 bits
#endif

int main(int argc, char ** argv)

  if (argc != 2)
    cerr << "Usage: " << argv[1] << " <file>" << endl;
  else 
    ifstream in(argv[1], ios::binary);

    if (!in.is_open())
      cerr << "Cannot open " << argv[1] << endl;
    else 
      in.seekg(0, ios::end);

      size_t n = (size_t) in.tellg() / 6;
      vector<uint64_t> values(n);
      uint64_t * p = values.data(); // for performance
      uint64_t * psup = p + n;

      in.seekg(0, ios::beg);

      int i = 1;

      if (*((char *) &i)) 
        // little endian
        unsigned char s[6];
        uint64_t v = 0;

        while (p != psup) 
          if (!in.read((char *) s, 6))
            return -1;
          ((char *) &v)[0] = s[5];
          ((char *) &v)[1] = s[4];
          ((char *) &v)[2] = s[3];
          ((char *) &v)[3] = s[2];
          ((char *) &v)[4] = s[1];
          ((char *) &v)[5] = s[0];
          *p++ = v;
        
      
      else 
        // big endian
        uint64_t v = 0;

        while (p != psup) 
          if (!in.read(((char *) &v) + 2, 6))
            return -1;
          *p++ = v;
        
      

      cout << "file successfully read" << endl;

      sort(values.begin(), values.end());
      cout << "values sort" << endl;

      // DEBUG, DO ON A SMALL FILE ;-)
      for (auto v : values)
        cout << v << endl;
    
  

【讨论】:

谢谢布鲁诺和托马斯。你的回答真的很有帮助。我正在研究它们,如果我有更多问题,我会请你帮助我。 @Behzad BTW 不要忘记在优化模式下编译(例如-O2 for g++

以上是关于如何在 C++ 或 Python 中快速将二进制文件划分为 6 字节块? [关闭]的主要内容,如果未能解决你的问题,请参考以下文章

C++如何将一个存有数据的文本文件转换为二进制文件?

如何将 C++ 或 C 中的字符串转换为整数数组?

在python中将字符串保存到二进制文件

Android开发学习

如何将 Python Decimal 实例转换为 C++ double?

Python Pandas 如何从命令行读取 C++ 结构二进制文件