【问题标题】:Python shutil copyfile - missing last few linesPython shutil copyfile - 缺少最后几行
【发布时间】:2015-10-11 08:53:28
【问题描述】:

我经常会丢失我尝试使用 shutil copyfile 复制的文件的最后几 kb。

我做了一些研究,确实看到有人在这里问过类似的问题: python shutil copy function missing last few lines

但我使用的是 copyfile,它似乎使用了 with 语句...

with open(src, 'rb') as fsrc:
    with open(dst, 'wb') as fdst:
        copyfileobj(fsrc, fdst)

所以我很困惑更多的用户没有遇到这个问题,如果它确实是某种缓冲问题 - 我认为它会更广为人知。

我非常简单地调用 copyfile,不要认为我可能做错了什么,基本上按照我认为的标准方式来做:

copyfile(target_file_name,dest_file_name) 

但我每次都缺少文件的最后 4kb 左右。

我也没有接触过在shutil中调用的copyfile函数……

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)

所以我很茫然,但我想我将要学习一些关于刷新、缓冲或 with 语句的知识,或者......帮助!谢谢


致阿南德: 阿南德,我避免提及那些东西,因为我觉得这不是问题,但既然你问了......执行摘要是我正在从 FTP 抓取一个文件,检查该文件是否与我上次保存的文件不同复制,如果是,下载文件并保存副本。这是一个迂回的意大利面条代码,我猜是在我是一个真正纯粹的功利主义新手时​​编写的。它看起来像:

for filename in ftp.nlst(filematch):
    target_file_name = os.path.basename(filename)
    with open(target_file_name ,'wb') as fhandle:
    try:
        ftp.retrbinary('RETR %s' % filename, fhandle.write)
        the_files.append(target_file_name)
        mtime = modification_date(target_file_name)
        mtime_str_for_file = str(mtime)[0:10] + str(mtime)[11:13] + str(mtime)[14:16]    + str(mtime)[17:19] + str(mtime)[20:28]#2014-12-11 15:08:00.338415.
        sorted_xml_files = [file for file in glob.glob(os.path.join('\\\\Storage\\shared\\', '*.xml'))]
        sorted_xml_files.sort(key=os.path.getmtime)
        last_file = sorted_xml_files[-1]
        file_is_the_same = filecmp.cmp(target_file_name, last_file)
        if not file_is_the_same:
            print 'File changed!'
            copyfile(target_file_name, '\\\\Storage\\shared\\'+'datebreaks'+mtime_str_for_file+'.xml') 
        else:
            print 'File '+ last_file +' hasn\'t changed, doin nothin'
            continue

【问题讨论】:

  • 您能否展示更多代码,您是如何创建 target_file_name 的,以及您是如何创建 target_file 本身的?
  • 是否有一个特定的文件总是会发生这种情况?你在什么操作系统上,什么蟒蛇?你能发布一个它总是这样做的文件吗?您是在尝试写入网络驱动器还是什么?
  • 阿南德,我在帖子中回复了你,不知道该怎么做,因为评论的字符太多。
  • Joran,几乎每次都在发生。也许每几百个文件中就有一个可以正常工作。操作系统是windows 7 pro,python是2.7。是的,写入网络驱动器。这些文件是 XML 文件。基本上我经常会错过 XML 的最后 10-50 行。
  • @10mjg 尝试做 - fhandle.flush() 之后 - ftp.retrbinary('RETR %s' % filename, fhandle.write)

标签: python shutil file-copying


【解决方案1】:

这里的问题很可能是在执行该行时 -

ftp.retrbinary('RETR %s' % filename, fhandle.write)

这是使用 fhandle.write() 函数将数据从 ftp 服务器写入文件(名称为 - target_file_name),但当您调用 -shutil.copyfile 时 - fhandle 的缓冲区已没有完全刷新,所以在复制文件时会丢失一些数据。

为确保不会发生这种情况,您可以将 copyfile 逻辑从 with 块中移出 fhandle

或者您可以在复制文件之前调用fhandle.flush() 来刷新缓冲区。

我认为关闭文件会更好(将逻辑移出with 块)。示例 -

for filename in ftp.nlst(filematch):
    target_file_name = os.path.basename(filename)
    with open(target_file_name ,'wb') as fhandle:
        ftp.retrbinary('RETR %s' % filename, fhandle.write)
    the_files.append(target_file_name)
    mtime = modification_date(target_file_name)
    mtime_str_for_file = str(mtime)[0:10] + str(mtime)[11:13] + str(mtime)[14:16]    + str(mtime)[17:19] + str(mtime)[20:28]#2014-12-11 15:08:00.338415.
    sorted_xml_files = [file for file in glob.glob(os.path.join('\\\\Storage\\shared\\', '*.xml'))]
    sorted_xml_files.sort(key=os.path.getmtime)
    last_file = sorted_xml_files[-1]
    file_is_the_same = filecmp.cmp(target_file_name, last_file)
    if not file_is_the_same:
        print 'File changed!'
        copyfile(target_file_name, '\\\\Storage\\shared\\'+'datebreaks'+mtime_str_for_file+'.xml') 
    else:
        print 'File '+ last_file +' hasn\'t changed, doin nothin'
        continue

【讨论】:

  • 谢谢你,这个修复绝对是我遇到的症状的第一个也是最直接的修复。我还将考虑 nsilent22 的解决方案,它似乎从根本上来说可能更合理。欢迎任何想法。
  • nslient22 是相同的解决方案,在我们将文件发送到 copyfile() 函数之前关闭文件,方法是将复制文件(以及所有不依赖于文件句柄的逻辑)移到 with 之外块。
  • 啊,你是对的,我专注于“刷新”的想法 - 但你还指定了“为了确保不会发生这种情况,你可以将复制文件逻辑移出 with 块 for fhandle ."。非常感谢。
  • (我专注于同花顺的想法,因为我第一次看到你的评论,然后才看到这个答案。再次感谢)。
  • 哦,好的,抱歉,这只是为了测试这是否是确切的问题(我有预感,只是想确认一下)。
【解决方案2】:

您正在尝试复制未关闭的文件。这就是缓冲区没有被刷新的原因。将copyfileobj 移出with 块,以允许fhandle 关闭。

做:

with open(target_file_name ,'wb') as fhandle:
    ftp.retrbinary('RETR %s' % filename, fhandle.write)

# and here the rest of your code
# so fhandle is closed, and file is stored completely on the disk

【讨论】:

  • 我会在完成对我之前发现的 Anand 的冲洗解决方案的测试后立即查看这个问题,并且似乎也解决了这个问题。我不够聪明,不知道哪种解决方案更好/等等。你的似乎是一个更根本的解决方案。谢谢您,任何其他见解都值得赞赏。
  • 经过一些测试后也可以使用。哇,事后看来如此明显。谢谢。
【解决方案3】:

This 看起来有更好的方法来嵌套withs

with open(src, 'rb') as fsrc, open(dst, 'wb') as fdst:
        copyfileobj(fsrc, fdst)

我会尝试更多类似的东西。我不是专家,希望有更多知识的人可以提供一些见解。我最好的想法是内部 with 在外部之前关闭。

【讨论】:

  • 我很乐意尝试/测试它。也许我很天真,但是这不是很奇怪……shutil copyfile 可以被如此广泛地使用,但却不是最理想的/需要修复吗?
  • 是的,这很奇怪。如果这能解决问题,老实说我会有点惊讶,但with 块总是把我搞砸。我像菜鸟一样使用open()close() :)
猜你喜欢
  • 1970-01-01
  • 2018-02-25
  • 1970-01-01
  • 2017-06-16
  • 1970-01-01
  • 1970-01-01
  • 2012-10-06
  • 2013-08-29
相关资源
最近更新 更多