Jump to: navigation, search

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