Thread: Loop Control 'while(!feof(cfPtr))', Duplication of record problem

  1. #1
    DirtyHarry
    Guest

    Post Loop Control 'while(!feof(cfPtr))', Duplication of record problem

    Hi, I have the below 2 functions. One creates a record of a customer, the other displays the records. Problem, when I use
    ' while(!feof(cfPtr))' the display function reads the last record a second time and displays it a second time. I've tried to use ' while ((c = fgetc(cfPtr)) != EOF) ', this works in that it does not repeat the last record, but for some reason when displaying the record, the output of the account number is incorrect, I get an 11 (eg. 12079595552) digit number which pushes into the next field of the display and overwrites data. I am not very familiar with file I/O, just beginning, so maybe there is a glaringly obvious error, hope someone there has a suggestion, this really has me stuck.
    Thanks,
    H.

    [tag]void add_customer()
    {
    struct customer new_cust={0,"",0,0};

    logo();
    tyme();

    if((cfPtr=fopen("cust_file.txt","r+"))==NULL)
    {
    printf("File could not be opened.\n");
    }
    else
    {
    printf("\n\nNew Customer Account Number:\t%d\n\n", cust_counter);
    Sleep(1500);

    fseek(cfPtr,(cust_counter-1)*sizeof(struct customer),SEEK_SET);

    fread(&new_cust, sizeof(struct customer),1,cfPtr);

    if(new_cust.acct_num!=0) //Validation to avoid account overwrite
    {
    printf("\nAccount %d already contains information.\n",new_cust.acct_num);
    stall();
    }
    else
    {
    logo();
    tyme();
    printf("\n\nEnter Customer Name\n\n?");
    fscanf(stdin,"%s",new_cust.cust_name);

    new_cust.acct_num=cust_counter;

    fseek(cfPtr,(new_cust.acct_num-1)*sizeof(struct customer), SEEK_SET);

    fwrite(&new_cust, sizeof(struct customer),1,cfPtr);
    fclose(cfPtr);
    cls();
    }
    }

    }
    [/tag]

    [tag]
    void disp_cust_record()
    {
    int disp_counter=0;
    int c=0;

    if((cfPtr=fopen("cust_file.txt","r"))==NULL)
    {
    printf("File could not be opened.\n");
    }
    else
    {
    logo(); //Prints a logo
    tyme();
    printf("\n\n%-6s%-16s%-11s%15s\n", "Acct", "Customer Name", "B_Code Start", "B_Code End");

    // while(!feof(cfPtr))

    while ((c = fgetc(cfPtr)) != EOF)
    {
    fread(&new_cust,sizeof(struct customer),1,cfPtr);

    if(new_cust.acct_num!=0)
    {
    printf("%d\t%s\t%d\t%d\n", new_cust.acct_num,
    new_cust.cust_name, new_cust.barcode_start, new_cust.barcode_end);
    }
    }
    fclose(cfPtr);
    stall(); //Generates a pause
    }
    }

    [/tag]

  2. #2
    Registered User Vber's Avatar
    Join Date
    Nov 2002
    Posts
    807
    Read this and this

  3. #3
    End Of Line Hammer's Avatar
    Join Date
    Apr 2002
    Posts
    6,231
    To add...

    - fread/fwrite require the file to be opened in binary mode.

    - This:
    >>while ((c = fgetc(cfPtr)) != EOF)
    >>{
    >> fread(&new_cust,sizeof(struct customer),1,cfPtr);
    is a problem. Use the return code from the fread to determine when you have reached the end of the file.
    When all else fails, read the instructions.
    If you're posting code, use code tags: [code] /* insert code here */ [/code]

  4. #4
    Code Goddess Prelude's Avatar
    Join Date
    Sep 2001
    Posts
    9,897
    >- fread/fwrite require the file to be opened in binary mode.
    Since when? The C standard says nothing about the open mode for the stream passed to fread/fwrite and since they are basically wrappers that call fgetc/fputc size times for each object, it makes no sense to have such a restriction.

    -Prelude
    My best code is written with the delete key.

  5. #5
    ATH0 quzah's Avatar
    Join Date
    Oct 2001
    Posts
    14,826
    Originally posted by Prelude
    >- fread/fwrite require the file to be opened in binary mode.

    Since when? The C standard says nothing about the open mode for the stream passed to fread/fwrite and since they are basically wrappers that call fgetc/fputc size times for each object, it makes no sense to have such a restriction.

    -Prelude
    Maybe he was really tired. Or perhaps, he was on the phone.

    On a side note, I believe that currently the "b" attributes are just hold overs for compatibility with old compilers, and they don't really have any real use any more. IIRC, I remember reading that they've been depreciated in the standard to the point where they don't do anything any more. IE: Text and binary modes are identical. I'll see if I can find a reference unless someone wants to point it out ahead of time.

    Quzah.
    Last edited by quzah; 03-21-2003 at 02:43 PM.
    Hope is the first step on the road to disappointment.

  6. #6
    Registered User Codeplug's Avatar
    Join Date
    Mar 2003
    Posts
    4,981
    Text and binary modes definately aren't identical on DOS and Windows machines.

    gg

  7. #7
    ATH0 quzah's Avatar
    Join Date
    Oct 2001
    Posts
    14,826
    Originally posted by Codeplug
    Text and binary modes definately aren't identical on DOS and Windows machines.

    gg
    Prove it. I'm on a windows box here. I have MSVC++. The following produces the exact same file:
    Code:
    #include<stdio.h>
    #include<stdlib.h>
    int main( void )
    {
    	FILE * fpt = fopen( "blah.txt", "w" );
    	FILE * fpb = fopen( "blah.dat", "wb" );
    	struct foo {
    		signed char buf[BUFSIZ];
    		unsigned short int x;
    	} bar = { "blah blah blah blah blah", 12345 };
    	if( fpt == NULL || fpb == NULL )
    		exit( 0 );
    	fwrite( &bar, sizeof( struct foo ), 1, fpt );
    	fwrite( &bar, sizeof( struct foo ), 1, fpb );
    	fwrite( &bar, sizeof( struct foo ), 1, fpt );
    	fwrite( &bar, sizeof( struct foo ), 1, fpb );
    	fwrite( &bar, sizeof( struct foo ), 1, fpt );
    	fwrite( &bar, sizeof( struct foo ), 1, fpb );
    
    	printf("Fweee...");
    
    	fclose( fpt );
    	fclose( fpb );
    	return 0;
    }
    blah.txt:

    1.50KB (1,542 byts), 4,096 bytes on disk

    blah.dat:
    1.50KB (1,542 byts), 4,096 bytes on disk
    So again, exactly how are they different? Bytes are bytes. You can write a 't' in text mode, or a 116 decimal value, open both in a text editor, and they'll turn out identical.

    [edit]chaning "text file" to "text editor"[/edit]

    Quzah.
    Last edited by quzah; 03-21-2003 at 10:06 PM.
    Hope is the first step on the road to disappointment.

  8. #8
    Just Lurking Dave_Sinkula's Avatar
    Join Date
    Oct 2002
    Posts
    5,005
    >Prove it.
    Code:
    /* ... */bar = { "blah\nblah\nblah\nblah\nblah", 12345 };
    With newlines in the text, I see a difference (Windows).
    Code:
    C:\Projects\Toolkit\stdc\test>"Test.exe"
    Fweee...
    C:\Projects\Toolkit\stdc\test>dir blah.*
    03/21/2003  11:35p               1,554 blah.txt
    03/21/2003  11:35p               1,542 blah.dat
    7. It is easier to write an incorrect program than understand a correct one.
    40. There are two ways to write error-free programs; only the third one works.*

  9. #9
    ATH0 quzah's Avatar
    Join Date
    Oct 2001
    Posts
    14,826
    The difference you encounter is the operating automaticly inserting '\r' for every newline. Macintosh computers do not use '\n', IIRC, and only use '\r'. *NIX system only use '\n'. Windows use pairs.

    The difference is then not with the language itself, it is with the implementation.

    As I was initially trying to point out, as far as the C language is concerned, there is no difference between the two.

    Here is the information to which I was referring:
    The mode string can also include the letter ``b'' either
    as a third character or as a character between the charac-
    ters in any of the two-character strings described above.
    This is strictly for compatibility with ANSI C3.159-1989
    (``ANSI C'') and has no effect; the ``b'' is ignored.
    In otherwords, if you have a compliant compiler, it should have no effect.

    [edit]
    Again, the point is illustrated. The '\r' is automaticly discarded when reading in text mode. In effect, it exists only while on disk while under said OS.

    To illustrate the point, open the .txt version of the file in my code with the following two additions:
    Code:
    	fpt = fopen( "blah.txt", "r" );
    	do
    	{
    		x = fgetc( fpt );
    		if( x == '\r' )
    		{
    			printf("\\r inserted by OS.\n");
    			printf("You will never see this.\n");
    		}
    	}while( x != EOF );
    	fclose( fpt );
    
    	fpb = fopen( "blah.txt", "rb" );
    	do
    	{
    		x = fgetc( fpb );
    		if( x == '\r' )
    			printf("\\r inserted by OS.\n");
    	}while( x != EOF );
    	fclose( fpb );
    [/edit]

    Quzah.
    Last edited by quzah; 03-22-2003 at 02:24 AM.
    Hope is the first step on the road to disappointment.

  10. #10
    Registered User Codeplug's Avatar
    Join Date
    Mar 2003
    Posts
    4,981
    Yes, quzah, there is not a difference between text and binary modes for *NIX machines, which all your *NIX reference material pointed out. POSIX even goes on to say:
    The character 'b' shall have no effect, but is allowed for ISO C standard conformance.
    The fact still remains, however, if you are not writting "text" to a file and you want your code to work correctly on a DOS/Windows machine, you must use "b". If you don't use "b", then whenever a 0x0A byte is encountered, a 0x0D byte will be inserted before it to make it look like a DOS/Windows newline.

    gg

  11. #11
    Registered User Codeplug's Avatar
    Join Date
    Mar 2003
    Posts
    4,981
    You're absolutely right Salem, but that's not the whole story. If you have fixed-sized records and you want to seek to a particular record, then you are SOL if you don't use binary mode (on DOS/Windows machines).

    So, as long as you are consistent and you don't need to seek based on a record size, then text or binary mode does not make a difference.

    Here is some code to show what I mean:
    Code:
        FILE    *f1;
        char data[6] = {0x0a,0x0a,0x55,0x55,0x0a,0x0a};
        char data2[6] = {0};
    
        f1 = fopen("xx1.bin","w");
        // write 3 records
        fwrite(data,sizeof(data),1,f1);
        fwrite(data,sizeof(data),1,f1);
        fwrite(data,sizeof(data),1,f1);
        fclose(f1);
    
        f1 = fopen("xx1.bin","r");
    
        // seek to the 2nd record
        fseek(f1,sizeof(data),SEEK_SET);
        fread(data2,sizeof(data2),1,f1);
        if (memcmp(data,data2,sizeof(data2)) == 0) 
        {
            printf("Compare OK\n");
        }
        else
        {
            printf("This printf will be executed\n");
        }
    
        fclose( f1 );
    gg

  12. #12
    End Of Line Hammer's Avatar
    Join Date
    Apr 2002
    Posts
    6,231
    >>Maybe he was really tired.
    Nope

    >>Or perhaps, he was on the phone.
    Maybe, I can't remember, but it was a busy day

    >>I believe that currently the "b" attributes are just hold overs for compatibility with old compilers
    >>as far as the C language is concerned, there is no difference between the two
    Not so, read on.

    >>http://www.rt.com/man/fopen.3.html
    This is a Unix man page, and as such can have a biased opinion on life.


    OK, enough of the quotes, let's get down to business. When I wrote my original post, my brain was thinking faster than I could type, and unfortunately I never got to finish my sentence. This is where the confusion comes from. I don't need to go into any details of fwrite/fread as they've already been covered by everyone in this thread (heavy-weight thread it is too, eh?!). The problem, comes from the OP's use of fseek() on the file stream, that is used for fread() and fwrite(). They are quite clearly attempting to open the file, fseek() to a specific struct instance, and then fread() it in; and to do that they must use a binary stream. fseek() requires different parameters depending on the stream mode. To save me waffling on about it, here's the C99 rules for it:
    Code:
    ISO/IEC 9899:1999 (E)
    7.19.9.2 The fseek function
    
    1 #include <stdio.h>
    int fseek(FILE *stream, long int offset, int whence);
    
    3 For a binary stream, the new position, measured in characters from the beginning of the
    file, is obtained by adding offset to the position specified by whence. The specified
    position is the beginning of the file if whence is SEEK_SET, the current value of the file
    position indicator if SEEK_CUR, or end-of-file if SEEK_END. A binary stream need not
    meaningfully support fseek calls with a whence value of SEEK_END.
    
    4 For a text stream, either offset shall be zero, or offset shall be a value returned by
    an earlier successful call to the ftell function on a stream associated with the same file
    and whence shall be SEEK_SET.
    So, you can see that the OPs use of fseek() is illegal; this is where I was coming from in my original post.

    I presume that reason for the difference in behaviour of fseek() is purely because of the problems generated by the OS's need to control how an EOL marker looks in an external file. For example, in text mode, how can you seek to a specific byte offset in the file, when you don't know for sure how many bytes you're traversing in the underlying system? Did the programmer ask for an N-byte offset with expanded EOLs or contracted EOLs, who knows? No-one, so seeking on text mode files is not feasible.

    So, in summary, I'll change my original line to:

    - fseek() requires the file to be opened in binary mode, when used in the context of the OPs code.

    Sorry for not stating the obvious in the first place, I can see I've caused you all to think hard for no reason.

    For the reader, here is some code to help your test this out.

    Code:
    #include <stdio.h>
    
    /*
     * Change this mode parameter to the desired one to see the different effects
     */
    #define MODE "r+"
    /* #define MODE "r+b" */
    
    struct foo 
    {
      int i;
    };
    
    int main(void)
    {
      struct foo bar[2] = {10, 20};
      FILE *fp;
      
      if ((fp = fopen("test.bin", MODE)) == NULL)
      {
        perror ("test.bin");
        return 0;
      }
      
      /* Write both structs */
      fwrite (bar, sizeof (bar), 1, fp);
    
      /* Point to second struct */
      fseek(fp, sizeof(bar[0]), SEEK_SET);
      
      /* Read second struct back into first struct in the array */
      fread (bar, sizeof (bar[0]), 1, fp);
      
      fclose(fp);
      
      printf ("This should say 20: %d\n", bar[0].i);
      
      return(0);
    }
    
    /*
    
    Output when run in binary mode:
    This should say 20: 20
    
    Output when run in text mode (might be different on your compiler):
    This should say 20: 5120
    
    */
    When all else fails, read the instructions.
    If you're posting code, use code tags: [code] /* insert code here */ [/code]

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. validation problem in a loop (newbie question)
    By Aisthesis in forum C++ Programming
    Replies: 11
    Last Post: 05-10-2009, 10:47 PM
  2. Replies: 8
    Last Post: 12-09-2008, 12:07 PM
  3. weired problem in while loop
    By avisik in forum C Programming
    Replies: 14
    Last Post: 12-01-2008, 01:41 PM
  4. Replies: 8
    Last Post: 12-01-2008, 10:09 AM
  5. Problem with while loop
    By Fyodorox in forum C++ Programming
    Replies: 3
    Last Post: 04-21-2002, 11:02 PM