【问题标题】:C++ most robust way to copy a fileC++ 最强大的文件复制方式
【发布时间】:2016-01-15 13:27:17
【问题描述】:

好的,所以我知道磁盘写入错误非常罕见,所以请看过去,因为我正在处理的数据非常重要(例如 SSID 很重要)。因此,我想使用绝对最小的内存量以绝对最强大的方式复制文件。到目前为止,这就是我所得到的。它占用了很多内存,但我找不到源。它的工作方式是反复检查无数次,直到得到确认的结果(它可能会大量增加错误的误报数量,但可能会大大降低实际错误的机会)。此外,底部的睡眠让您有时间使用 Windows 任务管理器分析程序的整体性能。


#include <cstdio>   // fopen, fclose, fread, fwrite, BUFSIZ
#include <cstdlib>
#include <unistd.h>
#include <iostream>

using namespace std;

__inline__ bool copy_file(const char* From, const char* To)
{
    FILE infile = (*fopen(From, "rb"));
    FILE outfile = (*fopen(To, "rwb+"));
    setvbuf( &infile, nullptr, _IONBF, 0);
    setvbuf( &outfile, nullptr, _IONBF, 0);

    fseek(&infile,0,SEEK_END);
    long int size = ftell(&infile);
    fseek(&infile,0,SEEK_SET);
    unsigned short error_amount;
    bool success;
    char c;
    char w;
    char l;

    for ( fpos_t i=0; (i != size); ++i ) {
        error_amount=0;
        fsetpos( &infile, &i );
        c = fgetc(&infile);
        fsetpos( &infile, &i );
        success=true;
        for ( l=0; (l != 126); ++l ) {
            fsetpos( &infile, &i );
            success = ( success == ( fgetc(&infile)==c ) );
        }
        while (success==false) {
            fsetpos( &infile, &i );
            if (error_amount==32767) {
                cerr << "There were 32768 failed attemps at accessing a part of the file! exiting the program...";
                return false;
            }
            ++error_amount;
            //cout << "an error has occured at position ";
            //printf("%d in the file.\n", (int)i);
            c = fgetc(&infile);
            fsetpos( &infile, &i );
            success=true;
            for ( l=0; (l != 126); ++l ) {
                fsetpos( &infile, &i );
                success = ( success == ( fgetc(&infile)==c ) );
            }
        }



        fsetpos( &infile, &i );
        fputc( c, &outfile);
        fsetpos( &outfile, &i );


        error_amount=0;
        w = fgetc(&infile);
        fsetpos( &outfile, &i );
        success=true;
        for ( l=0; (l != 126); ++l ) {
            fsetpos( &outfile, &i );
            success = ( success == ( fgetc(&outfile)==w ) );
        }
        while (success==false) {
            fsetpos( &outfile, &i );
            fputc( c, &outfile);
            if (error_amount==32767) {
                cerr << "There were 32768 failed attemps at writing to a part of the file! exiting the program...";
                return false;
            }
            ++error_amount;
            w = fgetc(&infile);
            fsetpos( &infile, &i );
            success=true;
            for ( l=0; (l != 126); ++l ) {
                fsetpos( &outfile, &i );
                success = ( success == ( fgetc(&outfile)==w ) );
            }
        }
        fsetpos( &infile, &i );
    }

    fclose(&infile);
    fclose(&outfile);

    return true;
}

int main( void )
{
    int CopyResult = copy_file("C:\\Users\\Admin\\Desktop\\example file.txt","C:\\Users\\Admin\\Desktop\\example copy.txt");

    std::cout << "Could it copy the file? " << CopyResult << '\n';

    sleep(65535);
    return 1;
}


那么,如果我的代码以最佳方式走在正确的轨道上,那么我的代码可以做些什么来改进它?但是,如果我的代码完全没有最好的解决方案,那么最好的解决方案是什么?请注意,这个问题本质上是关于检测罕见的磁盘写入错误,用于复制非常非常非常(等)重要数据的应用程序。

【问题讨论】:

  • 既然你的代码已经可以工作了,你应该把你的问题移到Code Review
  • 与您使用宏无关:不要“重命名”通用标准结构。没有人知道file 是什么,每个人都知道FILE 是什么。
  • 另外,你不知道如何例如fopen 创建它返回的结构指针。通过取消引用并复制到您自己的结构中,当您调用fclose 时,您可能会有未定义的行为。如果fopen调用malloc创建内存,fclose调用free怎么办?
  • 除了 C++ 风格的包含和输出之外,这是普通的旧 C。是什么让您将其标记为 C++ 问题?

标签: c++ copy memory-efficient robustness robust


【解决方案1】:

我只复制文件而不进行任何特殊检查,最后我会读取文件并将其哈希值与预期值进行比较。对于哈希函数,我会使用 MD5 或 SHA-1。

【讨论】:

  • 由于鸽巢原理,校验和并不完全可靠,因此使用它们会达到目的。
  • 另外,这个问题更多是关于避免罕见的磁盘写入错误。
  • 没有100%完美的解决方案。每个算法都有很小的机会报告复制成功,而实际上它失败了。即使您成功完成了写入,也不能完美保证一分钟后一个字节不会损坏。因此,我更喜欢基于标准的简单且易于理解的解决方案,而不是难以理解的复杂代码,并且可能更健壮也可能更健壮
  • 宾果游戏!!!!你完全正确!!!!!!!!!!!!!!!本质上,我想做的是大大减少错误复制的机会,为此,校验和不是要走的路。
  • 感谢您的参考:-)。我同意你的观点,它并不完美,但校验和为你提供了将可靠性提高到接近 100% 的机制。如果您认为 SHA-1 不足以满足您的目的,请使用强大的算法,甚至使用 10 个不同的校验和。我怀疑我们是否可以发明任何算法,比这更强大。
【解决方案2】:
#include <boost/filesystem.hpp>
#include <iostream>

int main()
{
    try
    {
        boost::filesystem::copy_file( "C:\\Users\\Admin\\Desktop\\example file.txt",
                                      "C:\\Users\\Admin\\Desktop\\example copy.txt" );
    }
    catch ( boost::filesystem::filesystem_error const & ex )
    {
        std::cerr << "Copy failed: " << ex.what();
    }
}

这将调用可以说是最强大的可用实现——操作系统提供的实现——并报告任何故障。


我的意思是:

一开始,您保存的数据最终损坏的可能性很小。

实际上可能存在问题的任何应用程序都应该在冗余存储上运行,也就是 RAID 阵列、执行校验和的文件系统(如BtrfsZFS)等,再次显着降低失败的机会 em>。

另一方面,在自主开发的 I/O 函数中执行复杂的操作会增加错误和/或误报的可能性非常

【讨论】:

  • 很好,但是检测罕见的磁盘写入错误呢?
  • @lolzerywowzery:驱动器固件检测到磁盘错误,向操作系统报告,向我的程序报告,向您报告“复制失败”。你可以再试一次。
  • @lolzerywowzery:有可能一场自然灾害会袭击你的房子并摧毁你的硬盘。
  • @lolzerywowzery:校验和是现代文件系统(如 ZFS、Btrfs)所做的,是的。 不要像您的示例代码所显示的那样笨拙、缓慢且容易出错。并且省去我的“pidgeonhole”论点。校验和冲突的几率比你我在同一天中奖的几率要小。
  • 这就像试图找到最小的数字。没有尽头。您不应该尽一切可能减少磁盘写入错误的机会,因为磁盘写入错误的可能性远低于系统可靠性的其他方面(我确信您的系统不仅仅是复制单个文件,对吗?)。在某些时候,您将不得不承认可靠性并不完美,但已经足够高,然后将注意力集中在其他事情上。
猜你喜欢
  • 2014-04-25
  • 2017-09-09
  • 1970-01-01
  • 2018-09-07
  • 1970-01-01
  • 2011-03-16
  • 1970-01-01
  • 2012-01-06
  • 1970-01-01
相关资源
最近更新 更多