J'ai besoin de comparer 2 fichiers et de renvoyer 1 s'ils sont identiques ou 0 sinon, mais la fonction renvoie toujours 0. Je ne sais pas pourquoi. Peut-être connaissez-vous différentes fonctions qui peuvent faire cela.
int compare(char *file_1, char *file_2)
{
FILE *data_1 = fopen(file_1,"r");
FILE *data_2 = fopen(file_2,"r");
char line1[1000];
char line2[1000];
while(fgets(line1, sizeof(line1), data_1)&&fgets(line2, sizeof(line2), data_2)){
if(strcmp(line1,line2)==0){
fclose(data_1);
fclose(data_2);
return 0;
}
}
fclose(data_1);
fclose(data_2);
return 1;
}
6 Réponses :
strcmp (line1, line2) == 0 signifie que line1 et line2 sont égaux , votre code suppose ils sont différents
Il y a une autre erreur, si un fichier commence par le contenu de l'autre vous considérez que les fichiers sont égaux (en supposant que vous ayez corrigé le strcmp)
Je vous encourage à vérifier le résultat de la fopen au cas où au moins l'un d'entre eux n'existe pas / ne peut pas être ouvert
une solution peut être:
int compare(char *file_1, char *file_2)
{
FILE *fp1 = fopen(file_1,"r");
if (fp1 == 0)
return 0;
FILE *fp2 = fopen(file_2,"r");
if (fp2 == 0) {
fclose(fp1);
return 0;
}
char line1[1000];
char line2[1000];
char * r1, * r2;
int result;
for (;;) {
r1 = fgets(line1, sizeof(line1), fp1);
r2 = fgets(line2, sizeof(line2), fp2);
if ((r1 == 0) || (r2 == 0)) {
result = (r1 == r2);
break;
}
if (strcmp(line1,line2) != 0) {
result = 0;
break;
}
}
fclose(fp1);
fclose(fp2);
return result;
}
il renvoie toujours 0
Dans un programme, j'obtiens toujours 1 dans l'autre toujours 0
J'ai encore changé ma définition, désolé. Celui-là fonctionne dans tous les cas
@bruno Pourquoi utiliseriez-vous return (r1 == r2); ?
@Michi je fais pour prendre en compte le fait que le fichier peut ne pas avoir la même taille mais avoir le même début, donc r1 == 0 mais r2! = 0 ou l'inverse
Vous devez signaler ce que vous retournez exactement. Il y a deux return 0; dans votre code.
il renvoie 1 lorsque les fichiers ont le même contenu, 0 si au moins un fichier ne peut pas être ouvert ou si leur contenu n'est pas égal
Comment savoir quel fp renvoie 0 ? Au fait Votre code Le chemin est conçu, si File_1 et File_2 a le contenu suivant Alors fonctionne Aimez ça .
Le code ne parvient pas à se comparer correctement lorsqu'un fichier contient caractères nuls en raison de l'utilisation de strcmp () .
@chux Vous avez raison, je suppose que ce sont des fichiers texte, les fgets ne sont pas non plus un bon choix pour un fichier non texte.
C n'exige pas que les fichiers texte n'aient pas de caractères nuls - même dans ce cas, il y en a peu dans les fichiers texte ASCII classiques. Remarque: (char) 0 est très courant dans les fichiers texte UTF-16. Peut-être que Linux a des exigences concernant les caractères nuls et les fichiers texte?
@chux Je suis presque sûr que vous comprenez ce que je veux dire à propos du fichier texte ;-) Quoi qu'il en soit, il est facile de changer la définition pour être compatible avec n'importe quel fichier, pensez-vous qu'il est préférable de le faire?
@bruno Utiliser fread () , IMO, est plus logique, car détaillé ici . Difficile de dire ce que les gens pensent parfois mieux: un code court clair qui gère bien les cas courants ou du code pour également gérer de nombreux cas périphériques. Ton appel.
Essayez de lire le fichier entier en une seule fois, la boucle fonctionnera jusqu'à ce que tout le fichier soit lu et s'il correspond une fois, il retournera toujours 0. Obtenez la taille du fichier quelque chose comme ceci et utilisez malloc et lisez>
fseek(fp, 0, SEEK_END); size = ftell(fp); fseek(fp, 0, SEEK_SET);
lire le fichier entier en une seule fois : c'est une mauvaise solution, vous perdez du temps à lire tout ce que vous pouvez même détecter avant qu'ils ne soient différents, et si les fichiers sont énormes, vous ne pouvez pas avoir assez de mémoire pour les lire
Eh bien, si ces fichiers ne sont pas trop volumineux, une bonne approche sera la suivante:
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <unistd.h>
void fileExistence( const char *fileName );
char *readFile( const char *const fileName );
size_t getFileSize ( FILE *const file );
int main ( void )
{
const char *const file_1 = "file1.txt";
const char *const file_2 = "file2.txt";
char *const getFile_01 = readFile( file_1 );
char *const getFile_02 = readFile( file_2 );
if (strcmp( getFile_01, getFile_02 ) == 0 )
{
printf( "Files are the same\n" );
}else
{
printf( "Files are not the same\n" );
}
free( getFile_01 );
free( getFile_02 );
return 0;
}
char *readFile( const char *const fName )
{
fileExistence( fName );
size_t size, length;
char *buffer;
FILE *file = fopen ( fName , "rb" );
if ( file == NULL )
{
fprintf( stderr, "Can't open output file %s!\n", fName );
exit( EXIT_FAILURE );
}
length = getFileSize( file );
if ( length == 0 )
{
printf( "Error, getFileSize()\n" );
}
buffer = malloc( length + 1 );
if ( buffer == NULL ){
printf( "Error, malloc().\n" );
exit( EXIT_FAILURE );
}
size = fread ( buffer , 1 , length, file );
if ( size != length ){
printf( "Error, fread().\n" );
exit( EXIT_FAILURE );
}
buffer[length] = '\0';
fclose ( file );
return buffer;
}
size_t getFileSize ( FILE *const file )
{
int fsk = fseek ( file , 0 , SEEK_END );
if ( fsk == -1 )
{
printf( "Error, fseek()\n" );
return 0;
}
long tel = ftell( file );
if ( tel < 0 || (unsigned long) tel >= SIZE_MAX )
{
printf( "Error, ftell()\n" );
return 0;
}
fsk = fseek (file , 0 , SEEK_SET );
if ( fsk == -1 )
{
printf( "Error, fseek()\n" );
return 0;
}
return ( size_t )tel;
}
void fileExistence( const char *const fileName )
{
if( access(fileName, F_OK ) )
{
printf("The File %s\t not Found\n",fileName);
exit( EXIT_FAILURE );
}
if( access(fileName, R_OK ) )
{
printf("The File %s\t cannot be readed\n",fileName);
exit( EXIT_FAILURE );
}
if( access( fileName, W_OK ) )
{
printf("The File %s\t it cannot be Edited\n",fileName);
exit( EXIT_FAILURE );
}
}
Où vous lisez les deux fichiers et les enregistrez dans deux tampons et utilisez strcmp ( ) pour les comparer.
Si vous avez besoin de comparer des fichiers sans majuscules, vous pouvez utiliser la fonction strcasecmp () qui se trouve dans strings.h code>
pourquoi utilisez-vous strcasecmp? en faisant cela, vous n'êtes pas sensible à la casse
non, je l'ai fait parce que votre solution ne fonctionne pas si vous ne pouvez pas tout lire, il n'y a aucune raison d'être bloqué par ce genre de problème
par exemple le cas produisant des fputs ("Memory error", stderr);
Pour accomplir cela, j'ai fait ces changements. Merci.
Pourquoi utiliser size_t length pour enregistrer le résultat de long ftell (file) ? 1) Le code manque le contrôle d'erreur et 2) un cast (size_t) ftell (file) peut perdre des informations.
Quelque chose comme long tel = ftell (fichier); if (tel <0 || tel> = SIZE_MAX) erreur (); else length = (size_t) tel; peut aider.
Oui tu as raison. Donc et donc j'ai besoin d'un casting pour size_t: D. Mais vous avez raison car il n'y a pas de -1 possible. De toute façon, il y aura un avertissement signalé comme comparaison d'expressions entières de significations différentes: 'long int' et 'long unsigned int' , ici - >> if (tel <0 || tel> = SIZE_MAX)
Je vais juste m'en tenir à vérifier -1 . J'ai mis à jour la réponse en fonction de vos suggestions.
Pour éviter l'avertissement, mais ne pas perdre la fonctionnalité, utilisez if (tel <0 || (unsigned long) tel> = SIZE_MAX) .
@chux Oui, j'y pensais aussi, mais je déteste vraiment les castings: | mais je vais l'ajouter.
continuons cette discussion dans le chat .
(size_t) tel> = SIZE_MAX n'a de sens que lorsque SIZE_MAX> = LONG_MAX . C n'assure pas cela cependant. (unsigned long) tel> = SIZE_MAX ne pose pas ce problème. IAC, UV pour l'amélioration.
@chux Oh, maintenant je vois le point, je vais mettre à jour. Quoi qu'il en soit, l'OP pose des questions sur Linux, j'ai donc également ajouté une nouvelle fonction, qui vérifie également l'existence du fichier.
vous pouvez comparer des fichiers caractère par caractère (ou octet par octet) pour obtenir un résultat plus rapide au cas où les fichiers ne seraient pas égaux:
int compare(char *file_1, char *file_2)
{
FILE *data_1 = fopen(file_1,"r");
FILE *data_2 = fopen(file_2,"r");
int ch1, ch2;
for (;;) {
ch1 = getc(data_1);
ch2 = getc(data_2);
if ((ch1 != ch2) || (ch1 == EOF)) break;
}
fclose(data_1);
fclose(data_2);
return (ch1 == ch2);
}
lire les fichiers char par char est très cher, plus lent que de lire bloc par bloc
ch1 et ch2 ne doivent pas être char mais int, actuellement vous ne pouvez pas détecter EOF
si les fichiers sont différents, vous ne fermez pas le fichier
vous avez aussi 2 fois if (ch1! = ch2) return 0
@bruno Oui char par char est plus lent que bloc par bloc, mais ce n'est pas très cher ( fgetc fait généralement des lectures tamponnées). Pour un code plus simple, le compromis peut en valoir la peine.
@bruno fixe le retour par pause et fermeture de fichier
@myxaxa considère ces deux lignes consécutives: if (ch1! = ch2) break; if (ch1 == EOF || ch2 == EOF) break; , si le premier test est faux cela signifie que ch1 == ch2, il est donc inutile de vérifier que ch1 et ch2 sont égaux à EOF. Peut être if ((ch1! = Ch2) || (ch1 == EOF)) break;
@myxaxa vous pouvez également simplifier si (ch1! = ch2) renvoie 0; return 1; être juste return (ch1 == ch2);
@myxaxa encore une fois, n'oubliez pas de changer ch1 et ch2 pour être int plutôt que char
Juste une remarque: parce que vous ne vérifiez pas ce que fopen renvoie lorsque les deux fichiers ne peuvent pas être lus (par exemple ils n'existent pas) vous retournez 1. J'ai préféré retourner 0, pour renvoyer 1 seulement quand tout est ok et les fichiers sont égaux.
Voici des solutions, un caractère par caractère lu / comparaisons (Inspiré de la réponse de myxaxa, mais avec des corrections de bogues) Et un autre bloc par bloc lecture / comparaisons. La vérification des erreurs a été ignorée en raison de la paresse, mais une implémentation robuste DOIT AVOIR UNE VÉRIFICATION D'ERREUR. (Voir commentaires)
real 0m0.353s user 0m0.083s sys 0m0.270s
Deuxième solution utilisant des lectures de blocs / comparaisons:
real 0m5.158s user 0m4.880s sys 0m0.277s
Runtimes pour char par char reads / comparaisons sur un fichier de 840 Mo comparé à lui-même:
#include <stdio.h>
#include <string.h>
#define BUFFSIZE 4096
int main(int argc, char **argv)
{
int equal = 1;
// TODO: check argc == 3
FILE *data_1 = fopen(argv[1],"r");
FILE *data_2 = fopen(argv[2],"r");
// TODO: check data_1 and data_2 !=NULL
for (;;)
{
char b1[BUFFSIZE];
char b2[BUFFSIZE];
size_t r1 = fread(b1, 1, BUFFSIZE, data_1);
size_t r2 = fread(b2, 1, BUFFSIZE, data_2);
if (r1 != r2)
{
equal = 0;
break;
}
// We only need to test r1, because at this point r1 == r2;
if (r1 == 0)
break;
if (memcmp(b1, b2, r1) != 0)
{
equal = 0;
break;
}
}
// TODO: check for read errors in data_1 and data_2 using ferror
fclose(data_1);
fclose(data_2);
if (equal)
printf("equal\n");
else
printf("not equal\n");
}
... et pour bloc par bloc sur le même fichier:
#include <stdio.h>
int main(int argc, char **argv)
{
int equal = 1;
// TODO: check argc == 3
FILE *data_1 = fopen(argv[1],"r");
FILE *data_2 = fopen(argv[2],"r");
// TODO: check data_1 and data_2 !=NULL
for (;;)
{
int ch1, ch2;
ch1 = fgetc(data_1);
ch2 = fgetc(data_2);
if (ch1 != ch2)
{
equal = 0;
break;
}
// We only need to test ch1, because at this point ch1 == ch2;
if (ch1 == EOF)
break;
}
// TODO: check for read errors in data_1 and data_2 using ferror
fclose(data_1);
fclose(data_2);
if (equal)
printf("equal\n");
else
printf("not equal\n");
}
Les deux tests ont plusieurs exécutions pour s'assurer que le fichier était déjà mis en cache
faire la vérification avec 2 fichiers avec différents premiers caractères :)
J'ai fait ça, mais le résultat était ennuyeux. Essentiellement le même runtime que / bin / true
Autres problèmes concernant la comparaison de fichiers non encore traités dans les réponses
Fichier de données avec '\0'^
Si un fichier contient un caractère nul , fgets () lira ce caractère comme tout autre caractère non de fin de ligne. Ensuite, un strcmp () suivant ne va pas comparer toute la ligne qui a été lue. Mieux vaut utiliser fread () / memcmp () pour éviter ce court-circuit.
Comparer en tant que texte ou binaire?
L'ouverture du fichier avec "r" comme dans fopen (file_1, "r") permet diverses traductions: fin de ligne, fin de fichier, octet- marques de commande.
Ouvrir avec "r" est logique pour comparer comme texte . Sinon, ouvrez le fichier en mode binaire "rb" . Utilisez fread () dans les deux cas.
Une ligne de texte avec "\ r \ n" dans un fichier et une ligne de texte avec "\ n" dans un autre fichier peut être comparable en mode texte , mais différer en mode binaire .
Tel quel balisé [linux] cependant, aucune traduction n'est attendue en mode texte .
Non comparable
Lors des lectures, une erreur de saisie peut se produire et rendre la comparaison inutile.
Exemple de code de comparaison
#include <stdbool.h>
#include <stdio.h>
#define FILE_COMPARE_N 1024
// 1: match
// 0: mis-match
// -1: failure
int stream_compare(FILE *f1, FILE *f2) {
unsigned char buf1[FILE_COMPARE_N];
unsigned char buf2[FILE_COMPARE_N];
size_t l1, l2;
do {
l1 = fread(buf1, sizeof buf1[0], FILE_COMPARE_N, f1);
if (ferror(f1))
return -1;
l2 = fread(buf2, sizeof buf2[0], FILE_COMPARE_N, f2);
if (ferror(f2))
return -1;
if (l1 != l2 || memcmp(buf1, buf2, l1) != 0)
return 0; // mis-match
} while (l1);
return 1; //match
}
int file_compare(const char *name1, const char *name2, bool as_text) {
FILE *f1 = fopen(name1, as_text ? "rb" : "r");
if (f1 == NULL)
return -1;
FILE *f2 = fopen(name2, as_text ? "rb" : "r");
if (f2 == NULL) {
fclose(f1);
return -1;
}
int compare = stream_compare(f1, f2);
fclose(f1);
fclose(f2);
return compare;
}
Le mode "texte vs binaire" n'est un problème que sur les plates-formes obscures, comme Windows
pourquoi ce n'est pas le cas? fgets met toujours le \ 0
@ G.M. prendre un café avec P.W ^^
l'expression
whiledevrait probablement utiliser||et non&&car si un fichier est plus court que l'autre, lewhile code > la boucle se terminera et elle retournera 1.@ChrisTurner oui, c'est ma deuxième remarque de ma réponse ;-)
Si les premières lignes correspondent et la seconde non, votre programme renverra toujours 0.
@ Don.Kielon J'ai édité ma réponse pour ajouter une proposition
Pourquoi ne calculez-vous pas le hachage md5 (par exemple en utilisant openssl / md5.h). Si les hachages sont égaux, vous savez que les fichiers sont égaux.
@ schorsch312 pour moi si les md5 sont différents les fichiers sont différents, s'ils sont égaux on ne sait pas si les fichiers sont égaux ou différents, un md5 ne peut pas couvrir tout le contenu d'un fichier, sinon cela signifie que le md5 est le réversible compression du fichier
Lisez Comment déboguer de petits programmes
@Don Kielon Modification annulée, sinon votre question n'a pas de sens.