65.9K
CodeProject 正在变化。 阅读更多。
Home

容错文件比较。

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.33/5 (2投票s)

2012年10月17日

CPOL

3分钟阅读

viewsIcon

13280

这是“大型文件在不稳定的硬件上的容错”的替代方案。

引言

复制大文件可能会导致错误,如原文所述,尤其是在所谓的“不靠谱”硬件上。我曾在一款特定的USB驱动器上遇到过这个问题,这让我很抓狂,因为我想要用这个
驱动器来备份大型容器。特别是如果你尝试恢复大型备份文件或想验证备份的完整性,你可能会对本文讨论的代码感兴趣。

在这个版本中,我将专注于文件比较。源代码仓库位于Github上

背景

原文提供了两个工具,一个用于容错复制,一个用于验证副本。文章不仅提供了关于如何比较此类文件的信息,还提供了关于如何使用WPF和Windows实现独特的UI效果的信息。如果你运行Windows并安装了.NET framework,你可能想看看原文。

如果你对原生C++版本感兴趣(或者你只是喜欢阅读其他版本),欢迎继续。本文将重点介绍C++代码,该代码将比较两个文件,并在它们
被怀疑相同时返回结果。

原生C++版本有一些优势。我不会讨论原生代码的任何性能优势,因为这不是C++实现的重点,我相信在这里可以忽略它们。

  • C++代码更具可移植性,因此如果可以使用C++编译器,你很可能可以轻松使用此代码。至少我怀疑在*ix系统上,Boost等常用库和C++编译器比Mono更常见。而且可能仍然存在无法使用Mono的系统。
  • 当其他一切都出错时,这样的系统实用程序可能是一个重要的工具。想象一下,你刚重新安装了操作系统,无法下载和安装.NET或Mono来运行这个程序,但你迫切需要使用这个工具来验证/复制你的备份。
  • 一个像样的UI绝对是我更喜欢的,但在某些情况下,你可能希望(甚至被迫)使用shell实用程序。
  • 这个程序的静态构建在没有任何依赖项的情况下运行。

比较文件内容的代码

这是我版本的文件比较代码的摘录。执行实际工作的部分。

c_buffer_bytes设置为1024 * 1024 * 10 (10 MB)。它应该是可配置的,我计划在后续版本中实现这一点。

m_max_retries默认设置为3,并且可以通过命令行设置。

bool CSequenceComparer::Compare(const std::string& first_file, const std::string& second_file)
{
    // [...] checking that files exist and are regular files (not directories)
    // [...] open files

    char* p_buffer;
    char* p_compare_buffer;
    // [...] allocating c_buffer_bytes of memory for each buffer
    
    short retries = 0;
    while ( ! input_stream.eof() )
    {
        streamoff input_offset = input_stream.tellg();
        input_stream.read(p_buffer, c_buffer_bytes);
        const streamsize num_of_input_bytes = input_stream.gcount();

        streamoff compare_offset = compare_stream.tellg();
        compare_stream.read(p_compare_buffer, c_buffer_bytes);
        const streamsize num_of_compare_bytes = compare_stream.gcount();

        bool equal = false;
        if ( num_of_input_bytes == num_of_compare_bytes )
        {
            if ( memcmp(p_buffer, p_compare_buffer, (size_t)num_of_input_bytes) == 0 )
            {
                equal = true;
            }
        }

        retries = equal ? 0 : (retries + 1);
        if (retries > m_max_retries)
        {
            break;
        }

        if (retries > 0)
        {
            // [...] seek to previously stored offsets to retry read
        }
    }

    delete [] p_buffer;
    delete [] p_compare_buffer;
    
    return retries == 0;
}

值得关注的点  

如果你很谨慎,你会发现上面的代码只要成功比较了文件或其部分内容,就会认为它们是相等的。因此在这种情况下,你可能需要添加代码来再次检查成功的比较,因为比较缓冲区设置得越低,误报的可能性就越高。

由于我想提供一个可移植的版本,所以我包含了boost文件系统库,以便代码更具可读性。在我的第一个在Windows下创建的版本中,我使用了一些仅限Windows的函数来检查文件是否可用。这是应该与Linux和Windows一起工作的部分(它在上面被注释掉了):

path filePath1(first_file);
path filePath2(second_file);
if ( !exists(filePath1) || !is_regular_file(filePath1) 
    || !exists(filePath2) || !is_regular_file(filePath2) )
{
    throw std::invalid_argument("Please provide regular file paths as arguments");
}

我还发现MSVC中的ifstream::seekg()方法会重置一些状态位。在Linux上运行时,我必须先调用ifstream::clear()才能继续。

历史 

提交到CodeProject 2012年10月17日。

© . All rights reserved.