Python 如何将原始文件系统的很大一部分复制到文件中?

Python 如何将原始文件系统的很大一部分复制到文件中?,python,windows,filesystems,buffer,disk,Python,Windows,Filesystems,Buffer,Disk,我正在使用一个神秘的数据收集文件系统。它有一个描述文件及其在磁盘上的精确偏移量的块,所以我知道每个文件的开始字节、结束字节和长度(以字节为单位)。目标是从物理磁盘中获取一个文件。它们是大文件,因此性能至关重要 以下是“有效”但效率很低的方法: import shutil, io def start_copy(startpos, endpos, filename="C:\\out.bin"): with open(r"\\.\PhysicalDrive1", 'rb') as src_f:

我正在使用一个神秘的数据收集文件系统。它有一个描述文件及其在磁盘上的精确偏移量的块,所以我知道每个文件的开始字节、结束字节和长度(以字节为单位)。目标是从物理磁盘中获取一个文件。它们是大文件,因此性能至关重要

以下是“有效”但效率很低的方法:

import shutil, io
def start_copy(startpos, endpos, filename="C:\\out.bin"):
    with open(r"\\.\PhysicalDrive1", 'rb') as src_f:
        src_f.seek(startpos)
        flength = endpos - startpos
        print("Starting copy of "+filename+" ("+str(flength)+"B)")
        with open(filename, 'wb') as dst_f:
            shutil.copyfileobj( io.BytesIO(src_f.read(flength)), dst_f )
        print("Finished copy of "+filename)
这很慢:
io.BytesIO(src_f.read(flength))
从技术上讲是可行的,但它会在写入目标文件之前将整个文件读入内存。因此,它需要的时间比它应该需要的要长得多

使用
dst\u f
直接复制将不起作用。(我假设)无法指定结束位置,因此复制不会停止

以下是一些问题,每个问题都可以解决:

  • 是否有采用起始/结束字节参数的复制库(或Windows 7的外部实用程序,可与
    子流程
    )一起使用
  • 是否可以创建一个
    copyfileobj
    可以使用的类似文件的对象,它只引用另一个类似文件的对象的一部分
  • io
    对象搜索超过某个端点时,是否可以引发异常
  • copyfileobj
    是否可以强制在驱动器的给定字节偏移量(一种“伪EOF”)处自然停止

显而易见的方法是将
写入文件

copyfileobj
的全部要点是为您缓冲数据。如果必须将整个文件读入
BytesIO
,则只需缓冲
BytesIO
,这是毫无意义的

因此,只需从
src_f
循环
read
ing一个适当大小的缓冲区,然后
write
将其写入
dst_f
,直到达到
flength
字节

如果你看一下(链接自),copyfileobj里面没有什么神奇的东西;这是一个微不足道的函数。从3.6开始(我认为它完全没有变化,因为
shutil
是在2.1左右添加的…),它看起来是这样的:

def copyfileobj(fsrc, fdst, length=16*1024):
    """copy data from file-like object fsrc to file-like object fdst"""
    while 1:
        buf = fsrc.read(length)
        if not buf:
            break
        fdst.write(buf)
您可以做同样的事情,只需跟踪读取的字节并在
flength
处停止:

def copypartialfileobj(fsrc, fdst, size, length=16*1024):
    """copy size bytes from file-like object fsrc to file-like object fdst"""
    written = 0
    while written < size:
        buf = fsrc.read(min(length, size - written))
        if not buf:
            break
        fdst.write(buf)
        written += len(buf)
def copypartialfileobj(fsrc、fdst、大小、长度=16*1024): “”“将大小字节从类文件对象fsrc复制到类文件对象fdst”“” 写入=0 书写时<大小: buf=fsrc.read(最小值(长度、大小-写入)) 如果不是buf: 打破 fdst写入(buf) 写入+=长度(buf)
为什么您需要使用
copyfileobj
而不仅仅是
写入
?@charjabug它不是异步工作的,而是由操作系统(以及驱动程序和驱动器电路)完成的缓冲这意味着,只要你按顺序在大小合适的块中阅读,它就可以很好地进行管道化处理。@charjabug如果你需要进一步加速,异步可能不是关键,至少不是直接的。如果其中一个磁盘是SSD或宽RAID条带(因此寻道时间不会像普通硬盘驱动器那样比吞吐量慢得可怕),只需执行4个线程,每个线程复制文件的1/4,就可以加快速度。或者,您可以使用
pywin32
调用一些适当的Win32 API函数,尽管我认为您必须处理重叠的I/O以获得任何加速。@charjabug另外,Python的大量stdlib模块被设计为可用的示例代码,而不仅仅是现成的工具。如果您转到任何模块的文档,并且有一个指向源代码的链接,单击它,您可以确切地看到它的工作原理。@charjabug我认为读一次写两次应该会更快…但不会那么快。毕竟,这是在优化过程中最快的部分(读取SSD,甚至可能从其缓存中取出),而不会影响最慢的部分(写入机械驱动器)。但是,并行执行两个副本(A->A&B->B同时在两个线程上执行,然后A->B和B->A在两个线程上执行)可以将您的时间减少近一半。(取决于总线争用是否与mechanical HD速度一样接近瓶颈,但我会尝试一下。)如果您已经知道目标文件大小,可以使用
fdst.truncate(size)
;内存通过
mdst=mmap.mmap(fdst.fileno(),0)映射目标;然后通过
fsrc.readinto(mdst)
复制数据。