2011-05-28 81 views
7

Por lo tanto, estoy tratando de escribir la función, que compara el contenido de dos archivos, pero yo no funciona.Comparar dos archivos

Quiero que devuelva 1 si los archivos son iguales y 0 si son diferentes.

ch1 y ch2 funciona como un búfer, y usé fgets para obtener el contenido de mis archivos.

Creo que hay algo mal con el puntero eof, pero no estoy seguro. Las variables FILE se dan dentro de la línea de comando.

P.S. Funciona con archivos pequeños con un tamaño inferior a 64 KB, pero se alimenta con archivos más grandes (películas de 700 MB, por ejemplo, o mp3 de 5 MB).

¿Alguna idea, cómo solucionarlo?

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; 
} 

EDITAR: He mejorado esta función con la inclusión de Your anwers. Pero solo está comparando el primer buffer solamente -> pero con excepción -> Me di cuenta de que deja de leer el archivo hasta que alcanza el carácter 1A (archivo adjunto). ¿Cómo podemos hacerlo funcionar?

EDIT2: Tarea resuelta (código de trabajo adjunto). Gracias a todos por la ayuda!

+6

¡No use 'strcmp' para comparar dos almacenamientos intermedios de datos binarios !!!! Se liberará tan pronto como vea un carácter terminador NULO. –

+0

¿qué quiere decir con "se molesta"? ¿Qué * exactamente * sucede? – Naveen

+0

Lo que quise decir es que el programa deja de funcionar después de la primera iteración. – Christoph

Respuesta

8

Dado que ha asignado sus matrices en la pila, están llenas de valores aleatorios ... no se han reducido a cero.

En segundo lugar, strcmp solo se comparará con el primer valor NULL, que, si se trata de un archivo binario, no necesariamente estará al final del archivo. Por lo tanto, realmente deberías estar usando memcmp en tus buffers. Pero nuevamente, esto dará resultados impredecibles debido al hecho de que sus búferes fueron asignados en la pila, por lo que incluso si compara los archivos que son iguales, el final de los buffers más allá del EOF puede no ser el mismo, entonces memcmp todavía informe resultados falsos (es decir, muy probablemente informe que los archivos no son los mismos cuando están debido a los valores aleatorios al final de los almacenamientos intermedios después del EOF de cada archivo respectivo).

Para solucionar este problema, primero debe medir la longitud del archivo examinando primero el archivo y viendo cuánto tiempo está en bytes y luego usando malloc o calloc para asignar los almacenamientos intermedios que va a utilizar para comparar y volver a llenar esos búferes con los contenidos reales del archivo. Entonces debería poder hacer una comparación válida de los contenidos binarios de cada archivo. También podrá trabajar con archivos de más de 64 KB en ese punto, ya que está asignando dinámicamente los búferes en tiempo de ejecución.

+0

He realizado mejoras. Pero aun así ... no funciona como yo quería. – Christoph

+0

Gracias por mencionar memcmp. – wil

+0

¿Alguna posibilidad de respuesta con un código incluido como ejemplo? –

7

Cuando los archivos son binarios, use memcmp not strcmp ya que \ 0 podría aparecer como datos.

3

Es mejor usar fread y memcmp para evitar problemas de \ 0 caracteres. Además, las comprobaciones !feof deberían ser realmente || en lugar de & & ya que hay una pequeña posibilidad de que un archivo es más grande que el otro y el archivo más pequeño es divisible por el tamaño del búfer .. código

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; 
} 
1

del interruptor se ve bien para mí, pero si quieres una exacta comparación la condición while y el retorno deben ser alterados:

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); 
} 
7

Aquí hay una solución C++. Parece apropiado ya que su pregunta está etiquetada como C++.El programa usa ifstream en lugar de FILE*. También le muestra cómo buscar en una secuencia de archivos para determinar el tamaño de un archivo. Finalmente, lee bloques de 4096 a la vez, por lo que los archivos grandes se procesarán como se espera.

// 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; 
} 
6

Si se puede renunciar a un poco de velocidad, aquí es una forma de C++ que requiere poco código:

#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())); 
} 

Mediante el uso de istreambuf_iterators se presiona la opción tamaño del búfer, lectura real, y el seguimiento de EF en la implementación de la biblioteca estándar. std::equal regresa cuando alcanza la primera falta de coincidencia, por lo que no debería ejecutarse más de lo necesario.

Esto es más lento que el cmp de Linux, pero es muy fácil de leer.