Сравните два файла - PullRequest
       13

Сравните два файла

10 голосов
/ 28 мая 2011

Я пытаюсь написать функцию, которая сравнивает содержимое двух файлов.

Я хочу, чтобы она возвращала 1, если файлы одинаковые, и 0, если разные.

ch1 и ch2 работает как буфер, и я использовал fgets для получения содержимого моих файлов.

Я думаю, что с указателем eof что-то не так, но я не уверен.FILE переменные задаются в командной строке.

PS Он работает с небольшими файлами размером менее 64 КБ, но не работает с большими файлами (например, с фильмами 700 МБ или 5 МБ файлов .mp3).

Есть идеи, как это решить?

int compareFile(FILE* file_compared, FILE* file_checked)
{
    bool diff = 0;
    int N = 65536;
    char* b1 = (char*) calloc (1, N+1);
    char* b2 = (char*) calloc (1, N+1);
    size_t s1, s2;

    do {
        s1 = fread(b1, 1, N, file_compared);
        s2 = fread(b2, 1, N, file_checked);

        if (s1 != s2 || memcmp(b1, b2, s1)) {
            diff = 1;
            break;
        }
      } while (!feof(file_compared) || !feof(file_checked));

    free(b1);
    free(b2);

    if (diff) return 0;
    else return 1;
}

РЕДАКТИРОВАТЬ: я улучшил эту функцию с включением ваших ответов.Но он сравнивает только первый буфер -> но с исключением -> Я понял, что он перестает читать файл, пока не достигнет 1А (вложенный файл).Как мы можем заставить его работать?

EDIT2: Задача решена (рабочий код прилагается).Спасибо всем за помощь!

Ответы [ 6 ]

18 голосов
/ 01 июня 2016

Если вы можете отказаться от небольшой скорости, вот способ C ++, который требует небольшого кода:

#include <fstream>
#include <iterator>
#include <string>
#include <algorithm>

bool compareFiles(const std::string& p1, const std::string& p2) {
  std::ifstream f1(p1, std::ifstream::binary|std::ifstream::ate);
  std::ifstream f2(p2, std::ifstream::binary|std::ifstream::ate);

  if (f1.fail() || f2.fail()) {
    return false; //file problem
  }

  if (f1.tellg() != f2.tellg()) {
    return false; //size mismatch
  }

  //seek back to beginning and use std::equal to compare contents
  f1.seekg(0, std::ifstream::beg);
  f2.seekg(0, std::ifstream::beg);
  return std::equal(std::istreambuf_iterator<char>(f1.rdbuf()),
                    std::istreambuf_iterator<char>(),
                    std::istreambuf_iterator<char>(f2.rdbuf()));
}

Используя istreambuf_iterators, вы помещаете выбор размера буфера, фактическое чтение и отслеживание eof в реализацию стандартной библиотеки. std::equal возвращается при достижении первого несоответствия, поэтому оно не должно выполняться дольше, чем нужно.

Это медленнее, чем cmp в Linux, но его очень легко прочитать.

9 голосов
/ 28 мая 2011

Когда файлы являются двоичными, используйте memcmp, а не strcmp, так как \ 0 может отображаться как данные.

8 голосов
/ 01 мая 2014

Вот решение C ++. Это кажется подходящим, поскольку ваш вопрос помечен как C++. Программа использует ifstream вместо FILE*. Он также показывает, как искать в файловом потоке, чтобы определить размер файла. Наконец, он считывает блоки по 4096 за раз, поэтому большие файлы будут обрабатываться как положено.

// g++ -Wall -Wextra equifile.cpp -o equifile.exe

#include <iostream>
using std::cout;
using std::cerr;
using std::endl;

#include <fstream>
using std::ios;
using std::ifstream;

#include <exception>
using std::exception;

#include <cstring>
#include <cstdlib>
using std::exit;
using std::memcmp;

bool equalFiles(ifstream& in1, ifstream& in2);

int main(int argc, char* argv[])
{
    if(argc != 3)
    {
        cerr << "Usage: equifile.exe <file1> <file2>" << endl;
        exit(-1);
    }

    try {
        ifstream in1(argv[1], ios::binary);
        ifstream in2(argv[2], ios::binary);

        if(equalFiles(in1, in2)) {
            cout << "Files are equal" << endl;
            exit(0);
        }
        else
        {
            cout << "Files are not equal" << endl;
            exit(1);
        }

    } catch (const exception& ex) {
        cerr << ex.what() << endl;
        exit(-2);
    }

    return -3;
}

bool equalFiles(ifstream& in1, ifstream& in2)
{
    ifstream::pos_type size1, size2;

    size1 = in1.seekg(0, ifstream::end).tellg();
    in1.seekg(0, ifstream::beg);

    size2 = in2.seekg(0, ifstream::end).tellg();
    in2.seekg(0, ifstream::beg);

    if(size1 != size2)
        return false;

    static const size_t BLOCKSIZE = 4096;
    size_t remaining = size1;

    while(remaining)
    {
        char buffer1[BLOCKSIZE], buffer2[BLOCKSIZE];
        size_t size = std::min(BLOCKSIZE, remaining);

        in1.read(buffer1, size);
        in2.read(buffer2, size);

        if(0 != memcmp(buffer1, buffer2, size))
            return false;

        remaining -= size;
    }

    return true;
}
8 голосов
/ 28 мая 2011

Поскольку вы разместили свои массивы в стеке, они заполнены случайными значениями ... они не обнуляются.

Во-вторых, strcmp будет сравниваться только с первым значением NULL,который, если это бинарный файл, не обязательно будет в конце файла.Поэтому вы действительно должны использовать memcmp в своих буферах.Но опять же, это даст непредсказуемые результаты из-за того, что ваши буферы были размещены в стеке, поэтому даже если вы сравните файлы, которые совпадают, конец буферов после EOF может не совпадать, поэтому memcmp будет по-прежнему сообщать о ложных результатах (т. е. он, скорее всего, будет сообщать, что файлы не совпадают, когда они из-за случайных значений в конце буферов после EOF каждого соответствующего файла).

Чтобы получитьДля решения этой проблемы вы должны сначала измерить длину файла, сначала пройдясь по файлу и посмотрев, как долго файл находится в байтах, а затем используя malloc или calloc для выделения буферов, которые вы собираетесь сравнивать.и снова заполните эти буферы фактическим содержимым файла.Тогда вы сможете сделать правильное сравнение двоичного содержимого каждого файла.Вы также сможете работать с файлами размером более 64 КБ, поскольку вы динамически распределяете буферы во время выполнения.

3 голосов
/ 25 октября 2011

Код коммутатора выглядит хорошо для меня, но если вы хотите точный Сравнение условий while и return должно быть изменено:

int compareFile(FILE* f1, FILE* f2) {
  int N = 10000;
  char buf1[N];
  char buf2[N];

  do {
    size_t r1 = fread(buf1, 1, N, f1);
    size_t r2 = fread(buf2, 1, N, f2);

    if (r1 != r2 ||
        memcmp(buf1, buf2, r1)) {
      return 0;  // Files are not equal
    }
  } while (!feof(f1) && !feof(f2));

  return feof(f1) && feof(f2);
}
3 голосов
/ 29 мая 2011

Лучше использовать fread и memcmp, чтобы избежать проблем с \ 0 символами. Кроме того, проверки !feof действительно должны быть || вместо &&, поскольку существует небольшая вероятность того, что один файл больше другого, а файл меньшего размера делится на размер буфера.

int compareFile(FILE* f1, FILE* f2) {
  int N = 10000;
  char buf1[N];
  char buf2[N];

  do {
    size_t r1 = fread(buf1, 1, N, f1);
    size_t r2 = fread(buf2, 1, N, f2);

    if (r1 != r2 ||
        memcmp(buf1, buf2, r1)) {
      return 0;
    }
  } while (!feof(f1) || !feof(f2));

  return 1;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...