The C Programming Language, 2nd Edition, by Kernighan and Ritchie
Exercise 7.06 on page 165
Write a program to compare two files, printing the first line where they differ.
Solution by Rick Dearman
/****************************************************** KnR 7-6 -------- Write a program to compare two files and print the first line where they differ. Author: Rick Dearman email: rick@ricken.demon.co.uk Note: This program prints ALL the lines that are different using the <> indicators used by the unix diff command. However this program will not cope with something as simple as a line being removed. In reality the program would be more useful if it searched forward for matching lines. This would be a better indicator of the simple removal of some lines. This has lead Richard Heathfield to track down a version of the "diff" command available on GNU/Linux systems. for more information go to the web site at: www.gnu.org ******************************************************/ #include <stdio.h> #include <string.h> #define MAXLINE 1000 void diff_line( char *lineone, char *linetwo, int linenumber ) { if(strcmp (lineone, linetwo) < 0 || strcmp (lineone, linetwo) > 0) printf( "%d<%s\n%d>%s\n", linenumber, lineone, linenumber, linetwo); } int main(int argc, char *argv[] ) { FILE *fp1, *fp2; char fp1_line[MAXLINE], fp2_line[MAXLINE]; int i; if ( argc != 3 ) { printf("differ fileone filetwo\n"); exit(0); } fp1 = fopen( argv[1], "r" ); if ( ! fp1 ) { printf("Error opening file %s\n", argv[1]); } fp2 = fopen( argv[2], "r" ); if ( ! fp2 ) { printf("Error opening file %s\n", argv[2]); } i = 0; while ( (fgets(fp1_line, MAXLINE, fp1) != NULL) && (fgets(fp2_line, MAXLINE, fp2) != NULL)) { diff_line( fp1_line, fp2_line, i ); i++; } return 0; }
Solution by Flippant Squirrel
/* Exercise 7-6 - write a program to compare two files, printing the first line * where they differ * * Note : I amended this a bit...if a file is shorter than the other, but is identical * up to that point, the program prints out "EOF" as the string that's not equal. * */ #include <stdio.h> #include <stdlib.h> #include <string.h> #define BUFF_SIZE 1000 /* uses fgets, removes the '\n' at the end of the string if it exists */ char *safegets(char *buffer, int length, FILE *file) { char *ptr; int len; if (buffer != NULL) { ptr = fgets(buffer, length, file); if (ptr != NULL) { len = strlen(buffer); if (len > 0) { if (buffer[len - 1] == '\n') { buffer[len - 1] = '\0'; } } } return ptr; } return NULL; } int main(int argc, char *argv[]) { FILE *leftFile, *rightFile; char buff1[BUFF_SIZE], buff2[BUFF_SIZE]; char *ptr1, *ptr2; unsigned long lineNum = 0; if (argc < 3) { fprintf(stderr, "Usage : 7_6 <path to file> <path to file>\n"); return 0; } if (!(leftFile = fopen(argv[1], "r"))) { fprintf(stderr, "Couldn't open %s for reading\n", argv[1]); return 0; } if (!(rightFile = fopen(argv[2], "r"))) { fprintf(stderr, "Couldn't open %s for reading\n", argv[2]); fclose(leftFile); /* RJH 10 Jul 2000 */ return 0; } /* read through each file, line by line */ ptr1 = safegets(buff1, BUFF_SIZE, leftFile); ptr2 = safegets(buff2, BUFF_SIZE, rightFile); ++lineNum; /* stop when either we've exhausted either file's data */ while (ptr1 != NULL && ptr2 != NULL) { /* compare the two lines */ if (strcmp(buff1, buff2) != 0) { printf("Difference:\n"); printf("%lu\t\"%s\" != \"%s\"\n", lineNum, buff1, buff2); goto CleanUp; } ptr1 = safegets(buff1, BUFF_SIZE, leftFile); ptr2 = safegets(buff2, BUFF_SIZE, rightFile); ++lineNum; } /* * if one of the files ended prematurely, it definitely * isn't equivalent to the other */ if (ptr1 != NULL && ptr2 == NULL) { printf("Difference:\n"); printf("%lu\t\"%s\" != \"EOF\"\n", lineNum, buff1); } else if (ptr1 == NULL && ptr2 != NULL) { printf("Difference:\n"); printf("%lu\t\"EOF\" != \"%s\"\n", lineNum, buff2); } else { printf("No differences\n"); } CleanUp: fclose(leftFile); fclose(rightFile); return EXIT_SUCCESS; }
Solution by Jose G. López (Category 1)
/* Compares two files, printing the first line where they differ. Note: Uses standard functions for managing files: fgetpos, fsetpos. With them we do not have limits about the length of a line. Only shows the differences compared on the file1. */ #include <stdio.h> #include <stdlib.h> /* exit() function */ FILE *open_file(char *s); int differ_files(FILE *fp1, FILE *fp2); int main(int argc, char *argv[]) { FILE *fp1, *fp2; if (argc != 3) { printf("Usage: [program] file1 file2\n"); exit(1); } else { fp1 = open_file(*++argv); fp2 = open_file(*++argv); differ_files(fp1, fp2); fclose(fp1); fclose(fp2); } return 0; } FILE *open_file(char *s) { FILE *fp; if ((fp = fopen(s, "r")) == NULL) { fprintf(stderr, "Can't open file %s\n", s); exit(2); } return fp; } int differ_files(FILE *fp1, FILE *fp2) { int c, d; int lin, retc; fpos_t posc; /* to store or set the file position */ retc = fgetpos(fp1, &posc); c = fgetc(fp1); d = fgetc(fp2); lin = 1; while (c == d && c != EOF && d != EOF) { if (c == '\n') { /* at the beginning of a new line */ retc = fgetpos(fp1, &posc); /* we save the file position */ lin++; } c = fgetc(fp1); d = fgetc(fp2); } /* if there are differences and we can set the file position */ if (c != d && !retc && !fsetpos(fp1, &posc)) { printf("Differences found!\nAt line %d - \"", lin); while ((c = fgetc(fp1)) != EOF && c != '\n') printf("%c", c); printf("\"\n"); } return (c != d); }
Solutions by codybartfast (Category 0)
Short Version
Without input/error checking:
#include <stdio.h> #include <string.h> #define MAXLINE (1 << 10) int main(int argc, char *argv[]) { char line1[MAXLINE], line2[MAXLINE]; FILE *file1 = fopen(argv[1], "r"), *file2 = fopen(argv[2], "r"); char *rslt1, *rslt2; int num = 0; do { num++; rslt1 = fgets(line1, MAXLINE, file1); rslt2 = fgets(line2, MAXLINE, file2); } while (rslt1 && rslt2 && !strcmp(rslt1, rslt2)); if (rslt1 != rslt2) printf("line: %d\n< %s---\n> %s", num, rslt1 ? rslt1 : "<EOF>\n", rslt2 ? rslt2 : "<EOF>\n"); return 0; }
Checked Version
With input/error checking:
#include <stdio.h> #include <stdlib.h> #include <string.h> #define MAXLINE (1 << 10) enum errors { NO_ERROR = 0, UNEXPECTED_ARG_COUNT, FILE_OPEN_ERROR, FILE_READ_ERROR }; static char *subnull(char *s); static int chkargc(int argc); static FILE *chkfopen(char *file, char *modes); static char *chkfgets(char *s, int n, FILE *iop, char *path); static void close(void); static FILE *file1, *file2; static char errormsg[MAXLINE]; int main(int argc, char *argv[]) { char line1[MAXLINE], line2[MAXLINE]; char *modes = "r"; char *path1, *path2; char *rslt1, *rslt2; int lncount = 0; chkargc(argc); file1 = chkfopen(path1 = argv[1], modes); file2 = chkfopen(path2 = argv[2], modes); do { lncount++; rslt1 = chkfgets(line1, MAXLINE, file1, path1); rslt2 = chkfgets(line2, MAXLINE, file2, path2); } while ((rslt1 != NULL) && (rslt2 != NULL) && !strcmp(rslt1, rslt2)); if (!(rslt1 == NULL && rslt2 == NULL)) printf("line: %d\n< %s---\n> %s", lncount, subnull(rslt1), subnull(rslt2)); close(); exit(NO_ERROR); } static char *subnull(char *s) { return (s == NULL) ? "<EOF>\n" : s; } int chkargc(int argc) { int expected = 3; if (argc == expected) return argc; fprintf(stderr, "error: Expected %d arguments, got %d\n", expected, argc); exit(UNEXPECTED_ARG_COUNT); } FILE *chkfopen(char *file, char *modes) { FILE *fp = fopen(file, modes); if (fp != NULL) return fp; sprintf(errormsg, "error: Failed to open file: '%s'", file); perror(errormsg); close(); exit(FILE_OPEN_ERROR); } char *chkfgets(char *s, int n, FILE *iop, char *path) { char *r = fgets(s, n, iop); if (!ferror(iop)) return r; sprintf(errormsg, "error: Failed to read file: '%s'", path); perror(errormsg); close(); exit(FILE_READ_ERROR); } void close(void) { if (file1 != NULL) fclose(file1); if (file2 != NULL) fclose(file2); }
Latest code on github
Solution by anonymous
I wrote a basic solution that does decent error checking. Here is my code
#include <stdio.h> #include <string.h> #include <stdlib.h> /* Exercise 7-6. Write a program to compare two files, printing the first line where they differ. */ #define MAXLINE 1000 int getline(char *line, int max, FILE *fp); int main(int argc, char *argv[]) { if (argc != 3) // wrong amount of arguments { printf("usage: diff file1 file2\n"); return 1; } FILE *fp1 = fopen(argv[1], "r"), *fp2 = fopen(argv[2], "r"); if (fp1 == NULL || fp2 == NULL) { printf("error: failed to open %s\n", fp1 == NULL ? argv[1] : argv[2]); exit(1); // calls fclose on open file pointers } int status1 = 1, status2 = 1, lineNum = 0; char line1[MAXLINE], line2[MAXLINE]; while (status1 != 0 && status2 != 0) { lineNum++; status1 = getline(line1, MAXLINE, fp1); status2 = getline(line2, MAXLINE, fp2); if (status1 == 0 && status2 == 0) printf("%s and %s are the same files!\n", argv[1], argv[2]); else if (status1 == 0) printf("%s file ended but %s file has:\n%d: %s", argv[1], argv[2], lineNum, line2); else if (status2 == 0) printf("%s file ended but %s file has:\n%d: %s", argv[2], argv[1], lineNum, line1); else if (strcmp(line1, line2) != 0) { printf("Files differ on line %d. %s has:\n%s%s has:\n%s", lineNum, argv[1], line1, argv[2], line2); break; } } fclose(fp1); fclose(fp2); return 0; } // read a line, return length int getline(char *line, int max, FILE *fp) { if (fgets(line, max, fp) == NULL) return 0; else return strlen(line); }