Thread: need some help with writing and reading to/from a file

  1. #1
    Registered User
    Join Date
    Nov 2011
    Posts
    18

    need some help with writing and reading to/from a file

    Hi,

    I have the following C "program" that I've been working on for a while now. It's pieced together from my book and some code tidbits I've found on various forums.

    It runs, but it is not working correctly.

    It generates duplicates when I add tool records and when listing tools, it generates output that looks like this:

    1 hammer -858993460 -858993460
    25 -858993460 -858993460
    50
    -858993460 -858993460
    1 hammer -858993460 -858993460
    25 -858993460 -858993460
    50
    -858993460 -858993460

    I've tried coding it in different ways, but they all seem to end up worse than this version.

    Here is the non-erring version. If anyone could help, I'd greatly appreciate it.

    Thanks!

    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <math.h>
    
    
    
    
    struct hardwareData 
    {
    	int recordNum;
    	char toolname[20];
    	int quantity;
    	int cost;
    }; 
    
    
    /* prototypes */
    int enterChoice( void );
    void textFile( FILE *readPtr );
    void updateRecord( FILE *fPtr );
    void newRecord( FILE *fPtr );
    void deleteRecord( FILE *fPtr );
    
    
    int main()
    {
      FILE *cfPtr; 
      cfPtr = fopen( "hardware.dat", "w" );
      int choice;
    
    
      
    	if ((cfPtr = fopen( "hardware.dat", "rb+" )) == NULL ) 
    	{
    		printf( "File could not be opened.\n" );
    	}
    	else 
    	{
    		while (( choice = enterChoice()) != 5 ) 
    		{
     			switch ( choice ) 
    			{
    				/* create text file from record */
     				case 1: 
    				textFile( cfPtr );
    				break;
    
    
    				/* update record */
     				case 2: 
    				updateRecord( cfPtr );
    				break;
    
    
    				/* create record */
     				case 3:
    				newRecord ( cfPtr );
    				break;
    
    
    				/* delete existing record */
     				case 4:
    				deleteRecord ( cfPtr );
    				break;
    
    
    				/* display message if user does not enter valid choice */
     				default:
    				printf( "Incorrect choice\n" );
    				break;
    
    
     			} 
    		} 
    		fclose( cfPtr ); 
    	} 
    	return 0; 
    } 
    
    
    void textFile( FILE *readPtr )
    {
    	FILE *writePtr;
    
    
    	struct hardwareData hardware = { 0, "", 0, 0 };
    
    
    	if ((writePtr = fopen( "hardware.txt", "w" )) == NULL ) 
    	{
    		printf( "File could not be opened.\n" );
    	} 
    	else
    	{ 
    		rewind( readPtr );
    		/* copy all records from random-access file to text file */
    		while ( !feof( readPtr ) ) 
    		{
     			fread( &hardware, sizeof( struct hardwareData ), 1, readPtr );
     			/* write single record to text file */
     			if ( hardware.recordNum != 0 ) 
    			{
    				fprintf(writePtr, "%d %s %d %d\n", hardware.recordNum, hardware.toolname, hardware.quantity, hardware.cost );
     			}
    			else
    			{
    				printf("File is empty.\n");
    			}
    		} 
    		fclose( writePtr );
    	} 
    	writePtr = fopen( "hardware.txt", "r" );
    
    
    	int toolNumber;
    	char toolName[20];
    	int toolQuanity;
    	int toolCost;
    
    
    	while ( !feof( writePtr ) ) {
    		fscanf( writePtr, "%d%[^0-9-]&d%d", &toolNumber, toolName, &toolQuanity, &toolCost );
    		printf( "%d %s %d %d\n", toolNumber, toolName, toolQuanity, toolCost );
    	}
    	fclose( writePtr );
    } 
    
    
    void updateRecord ( FILE *fPtr )
    {
    	int record; 
    	struct hardwareData hardware = { 0, "", 0, 0 };
    
    
    	printf( "Enter record to update (1-100): ");
    	scanf( "%d", &record );
    
    
    	
    	fseek( fPtr, ( record - 1 ) * sizeof( struct hardwareData ), SEEK_SET );
    
    
    	fread( &record, sizeof( struct hardwareData ), 1, fPtr );
    
    
    	if ( hardware.recordNum == 0) 
    	{
     		printf( "Record #%d has no information.\n", record );
    	}
    	else 
    	{ /* update record */
     		printf( "Enter new tool name, quantity,cost \n?" );
     		scanf( "%s%d%d", hardware.toolname, &hardware.quantity, &hardware.cost );
         
     		fseek( fPtr, ( record - 1 ) * sizeof( struct hardwareData ), SEEK_SET );
    		/* write updated record over old record */
     		fwrite( &hardware, sizeof( struct hardwareData ), 1, fPtr );
    	} 
    
    
    } 
    
    
    void deleteRecord( FILE *fPtr )
    {
    	struct hardwareData hardware; 
    	struct hardwareData blankRecord = { 0, "", 0,0 };
    	int recordNum; 
    
    
    	
    	printf( "Enter record number to delete (1-100): " );
    	scanf( "%d", &recordNum );
    
    
    	fseek( fPtr, ( recordNum - 1 ) * sizeof( struct hardwareData ), SEEK_SET );
    
    
    	fread( &hardware, sizeof( struct hardwareData ), 1, fPtr );
      
    	if ( hardware.recordNum==0 ) 
    	{
    		printf ( "Record %d does not exist.\n", recordNum );
    	} 
    	else 
    	{ 
    		fseek( fPtr, ( recordNum - 1 ) * sizeof( struct hardwareData ), SEEK_SET );
    		fwrite( &blankRecord, sizeof( struct hardwareData ), 1, fPtr );
    	} 
    } 
    
    
    void newRecord( FILE *fPtr )
    {
    	struct hardwareData hardware = { 0, "", 0, 0 };
    	int piece; 
    
    
    	printf( "Enter record number to create (1-100) : " );
    	scanf( "%d", &piece);
    	
    	fseek( fPtr, (piece - 1) * sizeof( struct hardwareData ), SEEK_SET );
    
    
    	fread( &hardware, sizeof( struct hardwareData ), 1, fPtr );
    
    
    	if ( hardware.recordNum != 0 ) 
    	{
    		printf( "Record already exists.\n", hardware.recordNum );
    	}
    	else 
    	{ 
    
    
    		printf( "Enter tool name, quantity, cost\n?" );
    		scanf("%s%d%d", hardware.toolname, &hardware.quantity, &hardware.cost);
    
    
    		hardware.recordNum = piece;
    
    
    		fseek(fPtr, (hardware.recordNum-1) * sizeof( struct hardwareData), SEEK_SET);
    
    
    		fwrite( &hardware, sizeof( struct hardwareData), 1, fPtr);
    	} 
    } 
     
    int enterChoice( void )
    {
    	int menuChoice;
    	printf( "\nEnter your choice\n"
    			"1 - List of all tools\n"
    			"2 - Update an existing tool\n"
    			"3 - Add a new tool\n"
    			"4 - Delete a tool\n"
    			"5 - End program\n? " );
    	scanf( "%d", &menuChoice ); 
    	return menuChoice;
    }

  2. #2
    Registered User
    Join Date
    May 2010
    Posts
    4,632
    You may want to insure your compiler is generating warnings:
    main.c||In function ‘textFile’:|
    main.c|122|warning: too many arguments for format|
    main.c||In function ‘newRecord’:|
    main.c|210|warning: too many arguments for format|
    ||=== Build finished: 0 errors, 2 warnings ===|
    These warnings may be one source of your problems.

    Jim

  3. #3
    Registered User hk_mp5kpdw's Avatar
    Join Date
    Jan 2002
    Location
    Northern Virginia/Washington DC Metropolitan Area
    Posts
    3,817
    Code:
    rewind( readPtr );
    /* copy all records from random-access file to text file */
    while ( !feof( readPtr ) )
    {
        fread( &hardware, sizeof( struct hardwareData ), 1, readPtr );
    Read: Why it's bad to use feof() to control a loop
    "Owners of dogs will have noticed that, if you provide them with food and water and shelter and affection, they will think you are god. Whereas owners of cats are compelled to realize that, if you provide them with food and water and shelter and affection, they draw the conclusion that they are gods."
    -Christopher Hitchens

  4. #4
    Registered User
    Join Date
    Nov 2011
    Posts
    18
    Quote Originally Posted by jimblumberg View Post
    You may want to insure your compiler is generating warnings:


    These warnings may be one source of your problems.

    Jim

    Thanks....but each of those functions takes one argument and I am only passing one argument to them. So I don't see how those errors could be getting generated.

    Thank you for taking the time to look at the code.

  5. #5
    Registered User
    Join Date
    Nov 2011
    Posts
    18
    Quote Originally Posted by hk_mp5kpdw View Post
    Code:
    rewind( readPtr );
    /* copy all records from random-access file to text file */
    while ( !feof( readPtr ) )
    {
        fread( &hardware, sizeof( struct hardwareData ), 1, readPtr );
    Read: Why it's bad to use feof() to control a loop
    Oh I see...ok I will try and replace "!feof" with "fgets(buf, sizeof(buf), fp) != NULL"

    Thank you!

  6. #6
    Registered User
    Join Date
    May 2010
    Posts
    4,632
    Thanks....but each of those functions takes one argument and I am only passing one argument to them. So I don't see how those errors could be getting generated.
    main.c|122|warning: too many arguments for format|
    This is line that is generating one of the warnings (line 122):
    Code:
            fscanf( writePtr, "%d%[^0-9-]&d%d", &toolNumber, toolName, &toolQuanity, &toolCost );
    This function takes multiple arguments. The first parameter is a file pointer, the second parameter is the format string, and then there should be one parameter for each of the format specifiers (%d). This error says the number of specifies does not match the number of parameters that follow. You have 3 specifiers (all ints %d) but you have 4 parameters (int*, C-string, int*, int*).

    You may want to review the documentation for scanf() or fscanf().

    Jim

  7. #7
    Registered User
    Join Date
    Nov 2011
    Posts
    18
    Quote Originally Posted by jimblumberg View Post
    This is line that is generating one of the warnings (line 122):
    Code:
            fscanf( writePtr, "%d%[^0-9-]&d%d", &toolNumber, toolName, &toolQuanity, &toolCost );
    This function takes multiple arguments. The first parameter is a file pointer, the second parameter is the format string, and then there should be one parameter for each of the format specifiers (%d). This error says the number of specifies does not match the number of parameters that follow. You have 3 specifiers (all ints %d) but you have 4 parameters (int*, C-string, int*, int*).

    You may want to review the documentation for scanf() or fscanf().

    Jim
    I see my error, I put "
    &d" instead of "%d"....thank you, that fixed the list tools function.

    But I am still getting some strange behavior.

    The Add Tool function is entering in 2 rows even if I only enter 1 row. For example, if I enter in "hammer 25 10", the program will write "1 hammer 25 10" to the file twice. And then, if I enter another tool, say a drill, it will delete one of the hammer entries, but put in 2 rows of the drill.

    I went through the add tool code, but I don't see anything that would do this.

    Thanks

  8. #8
    Registered User
    Join Date
    May 2010
    Posts
    4,632
    Post your current code. It would also be helpful if you pointed out where your "Add Tool function" is located. Did you fix the item pointed out in post #3, there is more than just one place you are making this mistake.

    Jim

  9. #9
    Registered User
    Join Date
    Nov 2011
    Posts
    18
    Hi,I fixed the "while ( !feof( readPtr ) )" issue...replacing it with "while ( fgets(buf, sizeof(buf), writePtr) != NULL)"

    The add tool function is called "void newRecord( FILE *fPtr )" and it is the second-to-last function.

    But I am not quite sure if that is where the problem is.

    It might be the "void textFile( FILE *readPtr )" function because that is the function that lists all the tools in the file, so maybe when I am copying from the .dat to the .txt file, something may be happening.

    Here is the current code:

    Code:
    #include <stdio.h>#include <stdlib.h>
    #include <math.h>
    
    
    
    
    struct hardwareData 
    {
        int recordNum;
        char toolname[20];
        int quantity;
        int cost;
    }; 
    
    
    /* prototypes */
    int enterChoice( void );
    void textFile( FILE *readPtr );
    void updateRecord( FILE *fPtr );
    void newRecord( FILE *fPtr );
    void deleteRecord( FILE *fPtr );
    
    
    int main()
    {
      FILE *cfPtr; 
      cfPtr = fopen( "hardware.dat", "w" );
      int choice;
    
    
      
        if ((cfPtr = fopen( "hardware.dat", "rb+" )) == NULL ) 
        {
            printf( "File could not be opened.\n" );
        }
        else 
        {
            while (( choice = enterChoice()) != 5 ) 
            {
                 switch ( choice ) 
                {
                    /* create text file from record */
                     case 1: 
                    textFile( cfPtr );
                    break;
    
    
                    /* update record */
                     case 2: 
                    updateRecord( cfPtr );
                    break;
    
    
                    /* create record */
                     case 3:
                    newRecord ( cfPtr );
                    break;
    
    
                    /* delete existing record */
                     case 4:
                    deleteRecord ( cfPtr );
                    break;
    
    
                    /* display message if user does not enter valid choice */
                     default:
                    printf( "Incorrect choice\n" );
                    break;
    
    
                 } 
            } 
            fclose( cfPtr ); 
        } 
        return 0; 
    } 
    
    
    void textFile( FILE *readPtr )
    {
        FILE *writePtr;
    
    
        struct hardwareData hardware = { 0, "", 0, 0 };
    
    
        if ((writePtr = fopen( "hardware.txt", "w" )) == NULL ) 
        {
            printf( "File could not be opened.\n" );
        } 
        else
        { 
            rewind( readPtr );
            /* copy all records from random-access file to text file */
            while ( !feof( readPtr ) ) 
            {
                 fread( &hardware, sizeof( struct hardwareData ), 1, readPtr );
                 /* write single record to text file */
                 if ( hardware.recordNum != 0 ) 
                {
                    fprintf(writePtr, "%d %s %d %d\n", hardware.recordNum, hardware.toolname, hardware.quantity, hardware.cost );
                 }
                else
                {
                    printf("File is empty.\n");
                }
            } 
            fclose( writePtr );
        } 
        writePtr = fopen( "hardware.txt", "r" );
    
    
        int toolNumber;
        char toolName[20];
        int toolQuanity;
        int toolCost;
        char buf[BUFSIZ] = "Garbage";
    
    
        //while ( !feof( writePtr ) ) {
        while ( fgets(buf, sizeof(buf), writePtr) != NULL)
        {
            //fgets(buf, sizeof(buf), fp) != NULL
            fscanf( writePtr, "%d%[^0-9-]%d%d", &toolNumber, toolName, &toolQuanity, &toolCost );
            printf( "%d %s %d %d\n", toolNumber, toolName, toolQuanity, toolCost );
        }
        fclose( writePtr );
    } 
    
    
    void updateRecord ( FILE *fPtr )
    {
        int record; 
        struct hardwareData hardware = { 0, "", 0, 0 };
    
    
        printf( "Enter record to update (1-100): ");
        scanf( "%d", &record );
    
    
        
        fseek( fPtr, ( record - 1 ) * sizeof( struct hardwareData ), SEEK_SET );
    
    
        fread( &record, sizeof( struct hardwareData ), 1, fPtr );
    
    
        if ( hardware.recordNum == 0) 
        {
             printf( "Record #%d has no information.\n", record );
        }
        else 
        { /* update record */
             printf( "Enter new tool name, quantity,cost \n?" );
             scanf( "%s%d%d", hardware.toolname, &hardware.quantity, &hardware.cost );
         
             fseek( fPtr, ( record - 1 ) * sizeof( struct hardwareData ), SEEK_SET );
            /* write updated record over old record */
             fwrite( &hardware, sizeof( struct hardwareData ), 1, fPtr );
        } 
    
    
    } 
    
    
    void deleteRecord( FILE *fPtr )
    {
        struct hardwareData hardware; 
        struct hardwareData blankRecord = { 0, "", 0,0 };
        int recordNum; 
    
    
        
        printf( "Enter record number to delete (1-100): " );
        scanf( "%d", &recordNum );
    
    
        fseek( fPtr, ( recordNum - 1 ) * sizeof( struct hardwareData ), SEEK_SET );
    
    
        fread( &hardware, sizeof( struct hardwareData ), 1, fPtr );
      
        if ( hardware.recordNum==0 ) 
        {
            printf ( "Record %d does not exist.\n", recordNum );
        } 
        else 
        { 
            fseek( fPtr, ( recordNum - 1 ) * sizeof( struct hardwareData ), SEEK_SET );
            fwrite( &blankRecord, sizeof( struct hardwareData ), 1, fPtr );
        } 
    } 
    
    
    void newRecord( FILE *fPtr )
    {
        struct hardwareData hardware = { 0, "", 0, 0 };
        int piece; 
    
    
        printf( "Enter record number to create (1-100) : " );
        scanf( "%d", &piece);
        
        fseek( fPtr, (piece - 1) * sizeof( struct hardwareData ), SEEK_SET );
    
    
        fread( &hardware, sizeof( struct hardwareData ), 1, fPtr );
    
    
        if ( hardware.recordNum != 0 ) 
        {
            printf( "Record already exists.\n", hardware.recordNum );
        }
        else 
        { 
    
    
            printf( "Enter tool name, quantity, cost\n?" );
            scanf("%s%d%d", hardware.toolname, &hardware.quantity, &hardware.cost);
    
    
            hardware.recordNum = piece;
    
    
            fseek(fPtr, (hardware.recordNum-1) * sizeof( struct hardwareData), SEEK_SET);
    
    
            fwrite( &hardware, sizeof( struct hardwareData), 1, fPtr);
        } 
    } 
     
    int enterChoice( void )
    {
        int menuChoice;
        printf( "\nEnter your choice\n"
                "1 - List of all tools\n"
                "2 - Update an existing tool\n"
                "3 - Add a new tool\n"
                "4 - Delete a tool\n"
                "5 - End program\n? " );
        scanf( "%d", &menuChoice ); 
        return menuChoice;
    }
    Last edited by 999cm999; 12-16-2011 at 10:03 AM. Reason: format code

  10. #10
    Registered User
    Join Date
    May 2010
    Posts
    4,632
    You are still using feof() to control a loop. Look at line 97 in the above code. You are still not using the fscanf() on line 126 correctly.
    Code:
            fscanf( writePtr, "%d%[^0-9-]%d%d", &toolNumber, toolName, &toolQuanity, &toolCost );
    Count the number of format specifiers in your format string, you will only find 3, yet you have 4 variables that you are trying to fill. Where is the format specifier for your C-string toolName?

    Please show a small sample of your data.

    Jim

  11. #11
    Registered User
    Join Date
    Nov 2011
    Posts
    18
    Quote Originally Posted by jimblumberg View Post
    You are still using feof() to control a loop. Look at line 97 in the above code. You are still not using the fscanf() on line 126 correctly.
    Code:
            fscanf( writePtr, "%d%[^0-9-]%d%d", &toolNumber, toolName, &toolQuanity, &toolCost );
    Count the number of format specifiers in your format string, you will only find 3, yet you have 4 variables that you are trying to fill. Where is the format specifier for your C-string toolName?

    Please show a small sample of your data.

    Jim
    Hi Jim,

    I tried replacing feof() on line 97 with "while ( fgets(buf, sizeof(buf), readPtr) != NULL)" but that caused my file to remain empty(nothing was getting written to it).

    Line 126...I looked up fscanf() and I think I am using it correctly. I have the file pointer, then the formatting(int, string, int, int) and then my 4 variables(int, string, int, int).

    After 4 rows...I get this when I list all tools:

    2 Drill 10 50
    3 Wrench 50 12
    4 Screwdriver 50 5
    4 Screwdriver 50 5
    4 Screwdriver 50 5

    But the file actually contains this:

    1 Hammer 100 10
    2 Drill 10 50
    3 Wrench 50 12
    4 Screwdriver 50 5
    4 Screwdriver 50 5

    So something is still wrong...

    thank you

  12. #12
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    Quote Originally Posted by jimblumberg View Post
    Count the number of format specifiers in your format string, you will only find 3, yet you have 4 variables that you are trying to fill. Where is the format specifier for your C-string toolName?
    There are 4.

    Code:
    %[^0-9-]
    reads a sequence of anything not containing 0-9 or '-' into a char*.

    @ 999cm999: You should step through this in a debugger, or throw a bunch of fprintf(stderr, ...)'s in so you can observe what is actually happening in sections where the problem could be occurring.
    Last edited by MK27; 12-16-2011 at 10:49 AM.
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  13. #13
    Registered User
    Join Date
    Sep 2006
    Posts
    8,868
    Code:
    #include <stdio.h> 
    
    int main(void) { //  
       int i,qty;
       double cost;
       char name[40];
       FILE *fp;
       fp=fopen("test.txt", "r");
       
       while((fscanf(fp, "%d %s %d %lf",&i,name,&qty,&cost))>2) { 
          printf("%d  %s %d %.2f\n",i,name,qty,cost);   
       } 
       fclose(fp);
       printf("\n");
       return 0;
    }
    fgets() also works well for this.

    I put in cost as a double - not sure if it should be or not.

  14. #14
    Registered User
    Join Date
    May 2010
    Posts
    4,632
    Why are you trying to use fgets()? Using fread() would make more sense. Instead of this:
    Code:
            /* copy all records from random-access file to text file */
            while ( !feof( readPtr ) )
            {
                 fread( &hardware, sizeof( struct hardwareData ), 1, readPtr );
    Something like:
    Code:
            size_t numToRead = sizeof(struct hardwareData);
            while ( fread( &hardware, sizeof( numToRead ), 1, readPtr ) == numToRead )
            {
                /* write single record to text file */
                 if ( hardware.recordNum != 0 )

    Jim

  15. #15
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    Quote Originally Posted by jimblumberg View Post
    Why are you trying to use fgets()?
    That is only used with the text output file, which is formatted with newlines (as per post #11, and textFile()), so fgets() is appropriate.

    However, this...

    Quote Originally Posted by 999cm999
    After 4 rows...I get this when I list all tools:
    Is a little confusing. "List all tools" how? I do not see a means except inside textFile(), in which case it should be the same as the text file itself.
    Last edited by MK27; 12-16-2011 at 11:17 AM.
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Reading out of and writing into the same file
    By Wiretron in forum C Programming
    Replies: 8
    Last Post: 12-30-2006, 02:04 PM
  2. reading and writing to file
    By jrb47 in forum C++ Programming
    Replies: 3
    Last Post: 12-04-2006, 07:35 AM
  3. File reading and writing
    By Noobwaker in forum C Programming
    Replies: 6
    Last Post: 03-08-2006, 02:28 AM
  4. reading & writing to a file
    By Micko in forum C++ Programming
    Replies: 5
    Last Post: 05-08-2004, 11:57 AM
  5. writing/reading txt file
    By Klinerr1 in forum C++ Programming
    Replies: 2
    Last Post: 04-01-2004, 09:34 PM