K&R2 , exercise 7.6

B

Ben Bacarisse

arnuld said:
your definition of prefix could be different but program, must not
fail. post contents of files on dpaste. I will check them and fix the
program.

file A: file B:
-----------------------
abc abc
def def
ghi

so file A has one more line than B but otherwise they are the same.
The code you posted does not report a difference between them and it
should.
 
B

Ben Bacarisse

I thought of adding NULL check in a new <if> condition but I am not
sure whether its a good idea:

/* compare 2 files */
void compare_files( FILE* pf1, FILE* pf2 )
{
char line1[ARRSIZE];
char line2[ARRSIZE];
const char *begin_line1, *begin_line2, *p1, *p2;


while( (p1 = fgets( line1, ARRSIZE, pf1 )) && (p2 = fgets( line2, ARRSIZE, pf2 )) )
{
begin_line1 = line1;
begin_line2 = line2;

if( strcmp( p1, p2 ) )
{
puts( begin_line1);
printf(" ------------------- \n");
puts( begin_line2);
return;
}
}

if( !p1 || !p2 )
{
/* print lines here */

}

Not quite right. It is tricky to cover all the cases -- when pf1 runs
out, p2 will contain the last thing that was put in it (which might be
nothing at all). I would force the reading of pairs of lines. &&
does not do that for you.
 
A

arnuld

You are still not seeing it ?
If you read line1 with success you will NOT read line2.

:\

Now I see it

I just changed the "while" condition to:

while( fgets( line1, ARRSIZE, pf1 ) && fgets( line2, ARRSIZE, pf2 ) )


and "if" condition to:

if( strcmp( p1, p2 ) )


it works fine now. Thanks
 
A

arnuld

This does not do what you think it does. In C, the following is
perfectly correct:

if (x == 0 || y/x > 1)

y/x will never be evaluated when x == 0 because the second part of the
'||' is not evaluated when the first part is found to be true
(non-zero). In your case, the second file is never read until first
file read *fails*. This is, absolutely, not what you wanted to
happen.


Thanks for this example.
 
U

user923005

On Wed, 23 Apr 2008 05:26:32 +0000, Richard Heathfield wrote:
Right. So that's a simple check to add, after the loop. If you've run out
of first file, you try to read a line from the second file. If that
doesn't work either because you've run out of file, then the files match..
Otherwise, they don't, and the second file is longer.

OK, I am able to run it successfully :). it is messiest code I ever wrote :(

void compare_files( FILE* pf1, FILE* pf2 )
{
  char line1[ARRSIZE];
  char line2[ARRSIZE];
  const char *begin_line1, *begin_line2, *p1, *p2;
  int files_equal, file1, file2, eof_file1, eof_file2;

  files_equal = 1;
  file1 = 1;
  file2 = 1;
  eof_file1 = 0;
  eof_file2 = 0;

  while( files_equal && file1 && file2 )
    {
      begin_line1 = p1 = fgets( line1, ARRSIZE, pf1 );
      begin_line2 = p2 = fgets( line2, ARRSIZE, pf2 );

      if( !p1 && !p2 )
        {
          file1 = file2 = 0;
        }
      else if( (!p1 && p2) )
        {
          files_equal = 0;
          eof_file1 = 1;
        }
      else if(  !p2 && p1 )
        {
          files_equal = 0;
          eof_file2 = 1;
        }
      else if( strcmp( p1, p2 ) )
        {
          files_equal = 0;
          eof_file1 = eof_file2 = 1;
        }
    }

  if( !files_equal )
    {
      if( eof_file1 )
        {
          puts(begin_line2);
        }
      else if( eof_file2 )
        {
          puts(begin_line1);
        }
      else if( eof_file1 && eof_file2 )
        {
          puts( begin_line1 );
          printf("\n-------------------------\n");
          puts( begin_line2 );
        }
    }

--http://lispmachine.wordpress.com/
my email ID is at the above address

Slightly reformatted it looks OK to me:

void compare_files(FILE * pf1, FILE * pf2)
{
char line1[ARRSIZE];
char line2[ARRSIZE];
const char *begin_line1,
*begin_line2,
*p1,
*p2;
int files_equal=1,
file1=1,
file2=1,
eof_file1=0,
eof_file2=0;

while (files_equal && file1 && file2) {
begin_line1 = p1 = fgets(line1, ARRSIZE, pf1);
begin_line2 = p2 = fgets(line2, ARRSIZE, pf2);

if (!p1 && !p2) {
file1 = file2 = 0;
} else if ((!p1 && p2)) {
files_equal = 0;
eof_file1 = 1;
} else if (!p2 && p1) {
files_equal = 0;
eof_file2 = 1;
} else if (strcmp(p1, p2)) {
files_equal = 0;
eof_file1 = eof_file2 = 1;
}
}

if (!files_equal) {
if (eof_file1) {
puts(begin_line2);
} else if (eof_file2) {
puts(begin_line1);
} else if (eof_file1 && eof_file2) {
puts(begin_line1);
printf("\n-------------------------\n");
puts(begin_line2);
}
}
}
 
A

arnuld

Does the smiley mean, that it does not work?

:D

no, it meant the program works.

Fails for me when one file is a prefix of the other :)


your definition of prefix could be different but program, must not
fail. post contents of files on dpaste. I will check them and fix the
program.
 
A

arnuld

file A: file B:
so file A has one more line than B but otherwise they are the same.
The code you posted does not report a difference between them and it
should.

yes, you are right. There is problem with the while() condition. What to
do about it ?


I thought of adding NULL check in a new <if> condition but I am not
sure whether its a good idea:

/* compare 2 files */
void compare_files( FILE* pf1, FILE* pf2 )
{
char line1[ARRSIZE];
char line2[ARRSIZE];
const char *begin_line1, *begin_line2, *p1, *p2;


while( (p1 = fgets( line1, ARRSIZE, pf1 )) && (p2 = fgets( line2, ARRSIZE, pf2 )) )
{
begin_line1 = line1;
begin_line2 = line2;

if( strcmp( p1, p2 ) )
{
puts( begin_line1);
printf(" ------------------- \n");
puts( begin_line2);
return;
}
}

if( !p1 || !p2 )
{
/* print lines here */

}
 
B

Ben Bacarisse

Richard Heathfield said:
Look, it's easy.

Well, I think so, but your way is getting bogged down.
same = 1;
faok = 1;
fbok = 1;
while(same && faok && fbok)
{
if(fgets(line1, ARRSIZE, pf1) == NULL)
{
faok = 0;
}
else if(fgets(line2, ARRSIZE, pf2) == NULL)
{
fbok = 0;
}
else
{
if(strcmp(line1, line2) != 0)
{
same = 0;
}
}
}

Read errors aside, at this point in the program either same is 1 (in which
case they're the same), or faok is 0, in which case your first file is
shorter but up to that point the files match, or fbok is 0, in which case
the second file is shorter but up to that point the files match.

I don't think so. If faok is 0 all you know is that file one is shorter
*or the same length* (in terms of lines) as file two.

You will need to read file two outside the loop to determine if it has
also run out. Now, that may have been your plan (you do not show the
tests at the end of the loop) but it seem way too complex to me.

If I were forced to read line by line (rather than char by char) I'd
do it this way:

int read_line(char *buf, size_t sz, FILE *fp)
{
*buf = 0;
return fgets(buf, sz, fp) != NULL;
}

void compare_files(FILE *fp1, FILE *fp2)
{
char l1[ARRSIZE], l2[ARRSIZE];

while (read_line(l1, sizeof l1, fp1) + read_line(l2, sizeof l2, fp2)) {
if (strcmp(l1, l2)) {
fputs(l1, stdout);
puts("-------------------");
fputs(l2, stdout);
return;
}
}
}

The auxiliary function read_line is trivial, but it does exactly
enough to make the various tests (and subsequent output) trivial.
 
A

arnuld

Not quite right. It is tricky to cover all the cases -- when pf1 runs
out, p2 will contain the last thing that was put in it (which might be
nothing at all). I would force the reading of pairs of lines. && does
not do that for you.


ok, I can force the reading of lines before while() and then I also need a
new while() loop to keep on reading till the NULL occurs.
 
A

arnuld

file A: file B:
-----------------------
abc abc
def def
ghi

so file A has one more line than B but otherwise they are the same.
The code you posted does not report a difference between them and it
should.


I came-up with this alternative code works the same way:


while( 1 )
{
p1 = fgets( line1, ARRSIZE, pf1 );
p2 = fgets( line2, ARRSIZE, pf2 );

begin_line1 = p1;
begin_line2 = p2;

if( strcmp( p1, p2 ) )
{
puts( begin_line1);
printf(" ------------------- \n");
puts( begin_line2);
return;
}
else
{
break;
}
}
 
A

arnuld

I am well aware that many expert programmers use this construct.
Nevertheless, whenever I see it in the hands of a neophyte, I can't help
but ask myself "how much thought has gone into the design of this loop?"
And the answer is usually "not enough".

:(


Look, it's easy.

no, its too hard to think.

same = 1;
faok = 1;
fbok = 1;
while(same && faok && fbok)
{
if(fgets(line1, ARRSIZE, pf1) == NULL)
{
faok = 0;
}
else if(fgets(line2, ARRSIZE, pf2) == NULL)
{
fbok = 0;
}
else
{
if(strcmp(line1, line2) != 0)
{
same = 0;
}
}
}

Read errors aside, at this point in the program either same is 1 (in which
case they're the same), or faok is 0, in which case your first file is
shorter but up to that point the files match, or fbok is 0, in which case
the second file is shorter but up to that point the files match.

Now mix in the possibility of read error. How will this affect things?


perhaps you could use a less confusing example. In your code, it will
never check the 2nd file once 1st file is NULL and at that time 2nd file
could contain 1 more line or could be NULL. If 2nd file is NULL then both
are same but if it has one more line then they are different and your code
does not check that. I tried it and it doe snot work either, same
semantic-bug that my code has:


void compare_files( FILE* pf1, FILE* pf2 )
{
char line1[ARRSIZE];
char line2[ARRSIZE];
const char *begin_line1, *begin_line2, *p1, *p2;
int files_equal, file1, file2;

files_equal = 1;
file1 = 1;
file2 = 1;



while( files_equal && file1 && file2 )
{
begin_line1 = p1 = fgets( line1, ARRSIZE, pf1 );
begin_line2 = p2 = fgets( line2, ARRSIZE, pf2 );

if( !p1 )
{
file1 = 0;
}
else if( !p2 )
{
file2 = 0;
}
else if( strcmp( p1, p2 ) )
{
files_equal = 0;
}
}

if( !files_equal )
{
puts( begin_line1 );
printf("::::::::::::::::");
puts( begin_line2 );
}
}
 
A

arnuld

Right. So that's a simple check to add, after the loop. If you've run out
of first file, you try to read a line from the second file. If that
doesn't work either because you've run out of file, then the files match.
Otherwise, they don't, and the second file is longer.


no, we have to add 2 checks:

1.) to check if both files are NULL.
2.) to check if the one was not NULL while the other was.


so it works out like this with Segmentation Fault :(



while( files_equal && file1 && file2 )
{
begin_line1 = p1 = fgets( line1, ARRSIZE, pf1 );
begin_line2 = p2 = fgets( line2, ARRSIZE, pf2 );

if( !p1 && !p2 )
{
file1 = file2 = 0;
}
else if( (!p1 && p2) || (!p2 && p1) )
{
files_equal = 0;
}
else if( !p1 )
{
file1 = 0;
}
else if( !p2 )
{
file2 = 0;
}
else if( strcmp( p1, p2 ) )
{
files_equal = 0;
}
}

if( !files_equal )
{
puts( begin_line1 );
printf("::::::::::::::::");
puts( begin_line2 );
}
 
A

arnuld

Right. So that's a simple check to add, after the loop. If you've run out
of first file, you try to read a line from the second file. If that
doesn't work either because you've run out of file, then the files match.
Otherwise, they don't, and the second file is longer.

OK, I am able to run it successfully :). it is messiest code I ever wrote :(



void compare_files( FILE* pf1, FILE* pf2 )
{
char line1[ARRSIZE];
char line2[ARRSIZE];
const char *begin_line1, *begin_line2, *p1, *p2;
int files_equal, file1, file2, eof_file1, eof_file2;

files_equal = 1;
file1 = 1;
file2 = 1;
eof_file1 = 0;
eof_file2 = 0;



while( files_equal && file1 && file2 )
{
begin_line1 = p1 = fgets( line1, ARRSIZE, pf1 );
begin_line2 = p2 = fgets( line2, ARRSIZE, pf2 );

if( !p1 && !p2 )
{
file1 = file2 = 0;
}
else if( (!p1 && p2) )
{
files_equal = 0;
eof_file1 = 1;
}
else if( !p2 && p1 )
{
files_equal = 0;
eof_file2 = 1;
}
else if( strcmp( p1, p2 ) )
{
files_equal = 0;
eof_file1 = eof_file2 = 1;
}
}


if( !files_equal )
{
if( eof_file1 )
{
puts(begin_line2);
}
else if( eof_file2 )
{
puts(begin_line1);
}
else if( eof_file1 && eof_file2 )
{
puts( begin_line1 );
printf("\n-------------------------\n");
puts( begin_line2 );
}
}
 
A

arnuld

Both those checks are described in the statement you quoted, so I don't
understand what you're getting at.




I thought by saying "So that's a simple check to add". Notice the "a
check", I thought <a check == 1 check>

:p
 
S

santosh

arnuld wrote:

If there is a function that takes a pointer:

void a_function( char* )

then I can pass either an array name or a pointer to it. If I pass an
array, then I can compute number of elements of array using:

sizeof( array ) / sizeof( array[0] );

inside the function.

No you can't. The array argument would "decay" to a pointer value,
loosing the associated size information in the process. Applying sizeof
will give you the size of the pointer instead -- not what you want.
Thus you must either pass the size of the array separately to the
function (i.e., as another parameter), or make it available at a known
place (this is not a good option), or encode the size as a part of the
array itself. There may be other more contorted methods too. With C the
only limit is your imagination. You could even write an array package
though it's usage is likely to be slower than using built-in arrays and
the interface would likely be painful without operator overloading.

Try this program and see:

#include <stdio.h>

static void fx(const char a[]) {
printf("sizeof a (in fx): %lu\n", (unsigned long)sizeof a);
return;
}

int main(void) {
const char a[] = "hello world!";
printf("sizeof a (in main): %lu\n", (unsigned long)sizeof a);
fx(a);
return 0;
}
but array will never be passed since it will get decayed into the
pointer to its 1st argument.

Pointer to it's first element you mean.

<snip>
 
A

arnuld

int read_line(char *buf, size_t sz, FILE *fp)
{
*buf = 0;
return fgets(buf, sz, fp) != NULL;
}


why you did <*buf = 0>

it will only make the 1st element of the array to be a zero, rest are
still same. Why not use this ?

memcopy( buf, '\0', sizeof(buff) / sizeof( buff[0] ))
 
A

arnuld

I don't think so. If faok is 0 all you know is that file one is shorter
*or the same length* (in terms of lines) as file two.
You will need to read file two outside the loop to determine if it has
also run out. Now, that may have been your plan (you do not show the
tests at the end of the loop) but it seem way too complex to me.


that was his plan but I did it with all tests inside the loop:


void compare_files( FILE* pf1, FILE* pf2 )
{
char line1[ARRSIZE];
char line2[ARRSIZE];
const char* *p1, *p2;
const char *begin_line1, *begin_line2;

int files_equal;
int file1, file2;
int eof_file1, eof_file2;

files_equal = 1;
file1 = file2 = 1;
eof_file1 = eof_file2 = 0;



while( files_equal && file1 && file2 )
{
begin_line1 = p1 = fgets( line1, ARRSIZE, pf1 );
begin_line2 = p2 = fgets( line2, ARRSIZE, pf2 );

if( !p1 && !p2 )
{
file1 = file2 = 0;
}
else if( (!p1 && p2) )
{
files_equal = 0;
eof_file1 = 1;
}
else if( !p2 && p1 )
{
files_equal = 0;
eof_file2 = 1;
}
else if( strcmp( p1, p2 ) )
{
files_equal = 0;
eof_file1 = eof_file2 = 1;
}
}


if( !files_equal )
{
if( eof_file1 )
{
puts(begin_line2);
}
else if( eof_file2 )
{
puts(begin_line1);
}
else if( eof_file1 && eof_file2 )
{
puts( begin_line1 );
printf("\n-------------------------\n");
puts( begin_line2 );
}
}
}

int read_line(char *buf, size_t sz, FILE *fp) {
*buf = 0;
return fgets(buf, sz, fp) != NULL;
}
}
void compare_files(FILE *fp1, FILE *fp2) {
char l1[ARRSIZE], l2[ARRSIZE];

while (read_line(l1, sizeof l1, fp1) + read_line(l2, sizeof l2,
fp2)) {
if (strcmp(l1, l2)) {
fputs(l1, stdout);
puts("-------------------");
fputs(l2, stdout);
return;
}
}
}
}
The auxiliary function read_line is trivial, but it does exactly enough
to make the various tests (and subsequent output) trivial.

that is exactly what I was looking for and this is the 1st time I have
seen someone using + in a loop :)

I am quite surprised at both how a problem can be solved using 2
different approaches and that people think in very different ways when
they look at a problem.
 
B

Barry Schwarz

On Wed, 23 Apr 2008 13:09:36 +0100, Ben Bacarisse wrote:

I don't think so. If faok is 0 all you know is that file one is shorter
*or the same length* (in terms of lines) as file two.
You will need to read file two outside the loop to determine if it has
also run out. Now, that may have been your plan (you do not show the
tests at the end of the loop) but it seem way too complex to me.


that was his plan but I did it with all tests inside the loop:


void compare_files( FILE* pf1, FILE* pf2 )
{
char line1[ARRSIZE];
char line2[ARRSIZE];
const char* *p1, *p2;
const char *begin_line1, *begin_line2;

int files_equal;
int file1, file2;
int eof_file1, eof_file2;

files_equal = 1;
file1 = file2 = 1;
eof_file1 = eof_file2 = 0;



while( files_equal && file1 && file2 )
{
begin_line1 = p1 = fgets( line1, ARRSIZE, pf1 );
begin_line2 = p2 = fgets( line2, ARRSIZE, pf2 );

if( !p1 && !p2 )
{
file1 = file2 = 0;
}
else if( (!p1 && p2) )
{
files_equal = 0;
eof_file1 = 1;
}
else if( !p2 && p1 )
{
files_equal = 0;
eof_file2 = 1;
}
else if( strcmp( p1, p2 ) )
{
files_equal = 0;
eof_file1 = eof_file2 = 1;
}
}


if( !files_equal )
{
if( eof_file1 )
{
puts(begin_line2);
}
else if( eof_file2 )
{
puts(begin_line1);
}
else if( eof_file1 && eof_file2 )

I think you are missing a couple of !. On the other hand, you could
delete the if clause completely and just go with an else.
{
puts( begin_line1 );
printf("\n-------------------------\n");
puts( begin_line2 );
}
}
}

int read_line(char *buf, size_t sz, FILE *fp) {
*buf = 0;
return fgets(buf, sz, fp) != NULL;
}
}
void compare_files(FILE *fp1, FILE *fp2) {
char l1[ARRSIZE], l2[ARRSIZE];

while (read_line(l1, sizeof l1, fp1) + read_line(l2, sizeof l2,
fp2)) {
if (strcmp(l1, l2)) {
fputs(l1, stdout);
puts("-------------------");
fputs(l2, stdout);
return;
}
}
}
}
The auxiliary function read_line is trivial, but it does exactly enough
to make the various tests (and subsequent output) trivial.

that is exactly what I was looking for and this is the 1st time I have
seen someone using + in a loop :)

I am quite surprised at both how a problem can be solved using 2
different approaches and that people think in very different ways when
they look at a problem.


Remove del for email
 
A

arnuld

So that, in the event of a line *not* being read, buf is still
guaranteed to contain a null-terminated string (provided that fgets
hasn't started reading anything into the array yet when it fails). It
isn't perfect, but it's the best that can be done if we're going to use
fgets.

If I am correct, it is not null terminated string, it is that the 1st element
is a null.

Right ?


memcopy( buf, '\0', sizeof(buff) / sizeof( buff[0] ))
Partly because memcpy has no 'o' in it,
:D


and partly because sizeof can't magically tell how big an array is
from looking at a pointer to its first element,

If there is a function that takes a pointer:

void a_function( char* )

then I can pass either an array name or a pointer to it. If I pass an
array, then I can compute number of elements of array using:

sizeof( array ) / sizeof( array[0] );

inside the function.


but array will never be passed since it will get decayed into the pointer
to its 1st argument.


lets say I pass pointer to array to the function:


char* pc = array;

then can I use the same sizeof() thing to calculate the number of elements
inside the function ?


but mostly because it's a complete waste of time.


well, I will not waste time then ;)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,994
Messages
2,570,222
Members
46,810
Latest member
Kassie0918

Latest Threads

Top