在处理备份文件时,经常需要验证备份文件的完整性。本文将介绍一种使用C++编写的文件比较工具的实现方法,该工具能够在没有.NET框架的环境中运行,从而在系统出现问题时也能使用。
在之前的版本中,提供了两个工具:一个用于错误容忍的复制,另一个用于验证复制。本文将专注于文件内容的比较。源代码可以在GitHub上找到。
尽管C++的性能优势不是本文的重点,但C++代码的可移植性更强,这意味着只要有C++编译器,就更容易使用这段代码。此外,C++编译器和常用库(如Boost)在*ix系统上可能比Mono更常见。
以下是文件比较代码的核心部分。设置了10MB的缓冲区大小,并默认设置了3次重试次数,可以通过命令行进行设置。
bool CSequenceComparer::Compare(const std::string& first_file, const std::string& second_file) {
// [...] 检查文件是否存在并且是常规文件(不是目录)
// [...] 打开文件
char *p_buffer;
char *p_compare_buffer;
// [...] 为每个缓冲区分配c_buffer_bytes的内存
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) {
// [...] 跳转到之前存储的偏移量以重试读取
}
}
delete[] p_buffer;
delete[] p_compare_buffer;
return retries == 0;
}
如果非常小心,会发现上述代码在比较文件或其部分时,只要第一次比较成功就认为它们相等。因此,可能需要添加代码来双重检查成功的比较,因为比较缓冲区设置得越低,误报的可能性就越高。
为了提供可移植的版本,包含了Boost文件系统库,以便于代码的可读性。在Windows下创建的第一个版本中,使用了Windows特有的函数来检查文件是否可用。这部分应该可以在Linux和Windows上工作(在上面已经注释掉了):
#include
boost::filesystem::path filePath1(first_file);
boost::filesystem::path filePath2(second_file);
if (!boost::filesystem::exists(filePath1) || !boost::filesystem::is_regular_file(filePath1) ||
!boost::filesystem::exists(filePath2) || !boost::filesystem::is_regular_file(filePath2)) {
throw std::invalid_argument("Please provide regular file paths as arguments");
}
还发现,在MSVC中,ifstream::seekg()方法会重置一些状态位。在Linux上运行时,必须在继续之前调用ifstream::clear()。