Reputation: 10230
I'm writing a unit test and need to compare a result file to a golden file. What's the easiest way to do so?
So far I have (for Linux environment):
int result = system("diff file1 file2");
They are different if result != 0
.
Upvotes: 16
Views: 15757
Reputation: 59811
This should work:
#include <string>
#include <fstream>
#include <streambuf>
#include <iterator>
bool equal_files(const std::string& a, const std::string& b) {
std::ifstream stream{a};
std::string file1{std::istreambuf_iterator<char>(stream),
std::istreambuf_iterator<char>()};
stream = std::ifstream{b};
std::string file2{std::istreambuf_iterator<char>(stream),
std::istreambuf_iterator<char>()};
return file1 == file2;
}
I suspect this to be not as fast as diff
, but it avoids calling
system
. It should be sufficient for a test-case, though.
Upvotes: 1
Reputation: 20266
Developing from DaveS's answer, and as first thing checking file size:
#include <fstream>
#include <algorithm>
bool compare_files(const std::string& filename1, const std::string& filename2)
{
std::ifstream file1(filename1, std::ifstream::ate | std::ifstream::binary); //open file at the end
std::ifstream file2(filename2, std::ifstream::ate | std::ifstream::binary); //open file at the end
const std::ifstream::pos_type fileSize = file1.tellg();
if (fileSize != file2.tellg()) {
return false; //different file size
}
file1.seekg(0); //rewind
file2.seekg(0); //rewind
std::istreambuf_iterator<char> begin1(file1);
std::istreambuf_iterator<char> begin2(file2);
return std::equal(begin1,std::istreambuf_iterator<char>(),begin2); //Second argument is end-of-range iterator
}
(I wonder if before rewinding, fileSize
could be used to create a more efficient end of stream iterator, which, by knowing the stream length, would allow std::equal
to process more bytes at the time).
Upvotes: 7
Reputation: 21113
If you want a pure c++ solution, I would do something like this
#include <algorithm>
#include <iterator>
#include <string>
#include <fstream>
template<typename InputIterator1, typename InputIterator2>
bool
range_equal(InputIterator1 first1, InputIterator1 last1,
InputIterator2 first2, InputIterator2 last2)
{
while(first1 != last1 && first2 != last2)
{
if(*first1 != *first2) return false;
++first1;
++first2;
}
return (first1 == last1) && (first2 == last2);
}
bool compare_files(const std::string& filename1, const std::string& filename2)
{
std::ifstream file1(filename1);
std::ifstream file2(filename2);
std::istreambuf_iterator<char> begin1(file1);
std::istreambuf_iterator<char> begin2(file2);
std::istreambuf_iterator<char> end;
return range_equal(begin1, end, begin2, end);
}
It avoids reading the entire file into memory, and stops as soon as the files are different (or at end of file). The range_equal because std::equal
doesn't take a pair of iterators for the second range, and isn't safe if the second range is shorter.
Upvotes: 24
Reputation: 161
Might be an overkill but you could build a table of hashes SHA-256 using boost/bimap and boost/scope_exit.
Here is a video how to do this by Stephan T Lavavej (starts at 8.15): http://channel9.msdn.com/Series/C9-Lectures-Stephan-T-Lavavej-Advanced-STL/C9-Lectures-Stephan-T-Lavavej-Advanced-STL-5-of-n
For more info about algorithm: http://en.wikipedia.org/wiki/SHA-2
Upvotes: 0
Reputation: 52679
one way to prevent reading both files is to pre-compute the golden file into a hash, eg a md5. Then you only have to check the test file. Note, this may be slower than just reading both files!
Alternatively, layer your checking - look at the file sizes, if they're different then the files are different and you can avoid a lengthy read-and-compare operation.
Upvotes: 2