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

  1. #16
    Banned
    Join Date
    Aug 2010
    Location
    Ontario Canada
    Posts
    9,547
    Study this code sample... it may help you understand records based, random access filing a bit better...

    Code:
    //random access file demo
    #include <stdlib.h>
    #include <stdio.h>
    #include <time.h>
    #include <ctype.h>
    #include <string.h>
    
    #define FNAME "random.dat"
    
    // test data struct
    struct t_Record
      { int number;
        char word[16]; }
      Record;
    
    
    
    ///////////////////////////////////////////////////////
    // Random Access File Handlers
    //
    
    // open or create the file
    FILE *FileOpen(char* Filename)
      { FILE* pFile;
        pFile = fopen(Filename,"rb+");
        if (!pFile)
          pFile = fopen(Filename,"wb+");
        return pFile; }
    
    
    // Write a record to the file
    int WriteRecord(FILE *File, int RecNum)
      { if( fseek(File, RecNum * sizeof(Record), SEEK_SET) == 0 )
          if ( fwrite(&Record,sizeof(Record),1,File) )
            return 1;
        return 0; }
    
    
    // read a record from the file
    int ReadRecord(FILE *File, int RecNum)
      { if( fseek(File, RecNum * sizeof(Record), SEEK_SET) == 0 )
          if ( fread(&Record,sizeof(Record),1,File) )
            return 1;
        return 0; }
    
    
    int AddRecord(FILE *File)
      { fseek(File,0,SEEK_END);
        fwrite(&Record,sizeof(Record),1,File);
        return (ftell(File) / sizeof(Record)) - 1; }
    
    
    
    //////////////////////////////////////////////////////////////
    // View a Record
    //
    
    int ViewRecord (FILE *File, int RecNum)
      { if (! ReadRecord(File,RecNum))
          { printf("Invalid record\n"); 
            return -1; }
        printf("-----\n");
        printf("Record        : %d\n",RecNum);
        printf("Number Value  : %d\n",Record.number);
        printf("Word Value    : %s\n",Record.word);
        printf("-----\n");  
        return RecNum; }
    
    
    
    //////////////////////////////////////////////////////////////
    // Add a new record
    //
    
    int AddNewData(FILE *File)
      { memset(&Record,0,sizeof(Record));
        printf("\nEnter a number : ");
        scanf("%d", &Record.number);
        printf("Enter a word : ");
        scanf(" %s",Record.word);
        return AddRecord(File); }
    
    
    
    //////////////////////////////////////////////////////////////
    // Edit a record
    //
    
    int EditRecord(FILE *File, int RecNum)
      { if (! ReadRecord(File,RecNum))
          { printf("Invalid record\n");  
            return -1; }
        printf("\n-----\n");
        printf("Record        : %d\n",RecNum);
        printf("Number Value  : %d\n",Record.number);
        printf("Word Value    : %s\n",Record.word);
        printf("-----\n");  
        
        do
          { while(getchar() != '\n');
            printf("Change Values: Number, Word or Save (N, W or S) ? ");
            switch (toupper(getchar()))
              { case 'N' :
                  printf("\nEnter new number : ");
                  scanf("%d",&Record.number);
                  break;
                case 'W' : 
                  printf("Enter new word : ");
                  scanf(" %15s", Record.word);
                  break;
                case 'S' :
                  if (WriteRecord(File,RecNum))
                    printf("\nRecord #%d updated\n",RecNum);
                  return RecNum; } }
        while(1);
        return -1; }
    
    
    ////////////////////////////////////////////////////////////////
    // List records
    // 
    
    void ListRecords(FILE *File )
      { int i = 0;
        printf("\nRecord     Number\tWord\n\n");
        while (ReadRecord(File,i))
          { printf("%3d%16d\t%s\n",i,Record.number,Record.word); 
            i++; }
        printf("\n\n"); }
    
    
    
    ////////////////////////////////////////////////////////
    // this is for demonstration purposes only
    // you would not do this in a real program
    void InitFile(FILE* File)
     { int x, y;
       memset(&Record,sizeof(Record),0);
       for (x = 0; x < 10; x++)
          { Record.number = rand();
            for (y = 0; y < ((Record.number % 15) + 1); y++)
              Record.word[y] = (rand() % 26) + 'a';
            Record.word[y] = 0;
            if (! WriteRecord(File,x))
              printf("Oh drat!");  } }
     
    
    
    //////////////////////////////////////////////////////////
    // program mains
    //
    int main (void)
      { int Rec = 0; // record number
        FILE *File;
    
        srand(time(NULL));
    
        File = FileOpen(FNAME); 
        if (!File)
          { printf("Curses foiled again!\n\n");
            exit(-1); }
    
        printf("Random Access File Demonstration\n\n");
     
        do
          { printf("Menu : Dummy, Add, Edit, View, List, Quit (D, A, E, V, L or Q) : ");
            switch(toupper(getchar()))
              { case 'D' :
                  printf("Creating dummy file of 10 entries\n");
                  InitFile(File);
                  break;
                case 'A' :
                  Rec = AddNewData(File);
                  printf("Record #%d Added\n\n", Rec);
                  break;              
                case 'E' :
                  printf("\nRecord number (-1 Cancels): ");
                  scanf("%d",&Rec);
                  if (Rec > -1)
                    EditRecord(File,Rec);
                  break;
                case 'V' :
                  printf("\nRecord number (-1 Cancels): ");
                  scanf("%d",&Rec);
                  if (Rec > -1)
                    ViewRecord(File,Rec);
                  break;
                case 'L' :
                  ListRecords(File);
                  break;
                case 'Q' :
                  fclose(File);
                  return 0; } 
                  
             while(getchar() != '\n'); }
        while (1); 
        return 0; }

  2. #17
    Registered User
    Join Date
    Nov 2011
    Posts
    18
    Quote Originally Posted by MK27 View Post

    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.
    Hi MK27, I'm sorry, you are right, that is confusing. What I should've written was, when I choose the "1 - List of all tools" option in the menu, it executes this function: void textFile( FILE *readPtr )

    Thanks

  3. #18
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    Quote Originally Posted by 999cm999 View Post
    Hi MK27, I'm sorry, you are right, that is confusing. What I should've written was, when I choose the "1 - List of all tools" option in the menu, it executes this function: void textFile( FILE *readPtr )
    Thanks
    Okay, so just to be clear, you are saying that this section of code from textFile() in post #9:
    Code:
        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 );
    }
    Does not print exactly what is in that file when you look at it in a text editor? If so, you should check the return value of fscanf. Try using this:

    Code:
            int check;
    [...]
            check = fscanf( writePtr, "%d%[^0-9-]%d%d", &toolNumber, toolName, &toolQuanity, &toolCost );
            printf( "(check = %d) %d %s %d %d\n", check, toolNumber, toolName, toolQuanity, toolCost );
    Scanf functions return the number of things actually read, so that will give you a clue if something didn't happen right.
    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

  4. #19
    Registered User
    Join Date
    Nov 2011
    Posts
    18
    Quote Originally Posted by MK27 View Post

    Does not print exactly what is in that file when you look at it in a text editor? If so, you should check the return value of fscanf. Try using this:

    Code:
            int check;
    [...]
            check = fscanf( writePtr, "%d%[^0-9-]%d%d", &toolNumber, toolName, &toolQuanity, &toolCost );
            printf( "(check = %d) %d %s %d %d\n", check, toolNumber, toolName, toolQuanity, toolCost );
    Scanf functions return the number of things actually read, so that will give you a clue if something didn't happen right.
    Thanks a nifty bit of debugging code. I inserted it and entered in one item. When I list the items in the file, I see this on screen:
    (check = 4) 1 hammer 50 10
    (check = -1) 1 hammer 50 10

    I am not sure what the "-1" could mean.

    BTW, the file is showing a duplicate. I am still not sure why it's inserting duplicates like that.

    Thanks

  5. #20
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    -1 is most likely EOF...which whoops! I just realized you are using both fgets and fscanf there. See the problem? Replace the fscanf with sscanf() -- this is what I thought you were doing (wasn't paying enough attention):

    Code:
            check = sscanf(buf, "%d%[^0-9-]%d%d", &toolNumber, toolName, &toolQuanity, &toolCost );
    This will read data from the buffer just read in by fgets.

    Probably you will eventually want to rewrite this without fgets, by initializing "check" to 0 and making the condition while(check != -1). Then you can go back to using fscanf directly. But leave it for now until you resolve the input/output issue, because w/ the fgets->sscanf set-up you can do more debugging.
    Last edited by MK27; 12-16-2011 at 12:47 PM.
    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

  6. #21
    Registered User
    Join Date
    Nov 2011
    Posts
    18
    Quote Originally Posted by MK27 View Post
    -1 is most likely EOF...which whoops! I just realized you are using both fgets and fscanf there. See the problem? Replace the fscanf with sscanf() -- this is what I thought you were doing (wasn't paying enough attention):

    Code:
            check = sscanf(buf, "%d%[^0-9-]%d%d", &toolNumber, toolName, &toolQuanity, &toolCost );
    This will read data from the buffer just read in by fgets.
    Ok, I replaced fscanf with sscanf and now I get this:

    (check = 4) 1 hammer 50 5
    (check = 4) 1 hammer 50 5

    So no more "-1"....but I am still getting a duplicate entry. I don't see what in my code is causing duplicates. I am only reading in line by line and writing line by line.....

    Thank you

  7. #22
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    1) after textFile() is called, look at the file in an editor to make sure it really has a duplicate. If so..
    2) add some debugging code into where you do the write, eg:

    Code:
                 if ( hardware.recordNum != 0 )
                {
                    fprintf(writePtr, "%d %s %d %d\n", hardware.recordNum, hardware.toolname, hardware.quantity, hardware.cost );
    fprintf(stderr, "wrote #%d, %s\n", hardware.recordNum, hardware.toolname);
                 }
    Use stderr with debugging because stdout is buffered and so may mislead WRT the order of actual events. I like to leave stuff like that un-indented because it's easy to spot and remove later.*

    If there are duplicates being written, they are probably in your .dat file too. That one is harder to eyeball because it will have null bytes and other non-ascii stuff in it; you need to use a "hexdump" type program. If you don't know what that is, ask and I can post some code that will do the same thing.

    * you will have to learn to use a real debugger eventually, but fprintf() is still often useful IMO.
    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

  8. #23
    Registered User
    Join Date
    Nov 2011
    Posts
    18
    Quote Originally Posted by MK27 View Post
    1) after textFile() is called, look at the file in an editor to make sure it really has a duplicate. If so..
    2) add some debugging code into where you do the write, eg:
    The text file does indeed display the duplicate:

    1 hammer 50 5
    1 hammer 50 5

    However the .dat file looks good. It's a dat file, so I can't see much, but I do see the word "hammer" entered in only once...not twice like in the text file. So something must be happening when I generate the text file.

  9. #24
    Registered User
    Join Date
    Nov 2011
    Posts
    18
    Quote Originally Posted by 999cm999 View Post
    The text file does indeed display the duplicate:

    1 hammer 50 5
    1 hammer 50 5

    However the .dat file looks good. It's a dat file, so I can't see much, but I do see the word "hammer" entered in only once...not twice like in the text file. So something must be happening when I generate the text file.
    My function that writes looks like this:

    Code:
    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);
    	} 
    }
    Should I be using fprintf instead of the fseek and fwrite?

    Thanks

  10. #25
    Registered User
    Join Date
    Nov 2011
    Posts
    18
    Quote Originally Posted by MK27 View Post
    Use stderr with debugging because stdout is buffered and so may mislead WRT the order of actual events. I like to leave stuff like that un-indented because it's easy to spot and remove later.*
    Ok I did some debugging and really haven't found anything new....just that it's duplicating. Now my output look like this:

    wrote #1, hammer
    wrote #1, hammer
    (check = 4) 1 hammer 50 5
    (check = 4) 1 hammer 50 5

    Thanks

  11. #26
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    Just noticed this in textFile():

    Code:
            while ( !feof( readPtr ) )
            {
                fread( &hardware, sizeof( struct hardwareData ), 1, readPtr );
                /* write single record to text file */
                if ( hardware.recordNum != 0 )
                {
                    fprintf(writePtr,
    I know somebody pointed out the problems with feof in post #3, and you said you replaced it with fgets(), which implies you may have duplicated the double read problem from earlier. Anyway, as is, the bit above will double write because feof will be set on the file handle after fread(), but not picked up by the while condition until after the fprintf.

    It'd be a good idea if you re-post the whole program as it is right now, that way I can compile and look at it here.

    In general, it seems to me that you have written too much code at once, without making sure that it all works. As a rule of thumb, especially at your stage, you should compile and test your code every 10 minutes or 20 lines and make sure it does what you expect, even if this means shimming stuff in temporarily. Working any other way does not save time in the long run. On the weekend, I wrote a tish load of code as part of a larger project that I could not test at all until it was finished, which I hate doing, but could not find a way around. I then spent 12-18 hours mon-tues-wed correcting and debugging the consequences. Avoid situations like that at all costs! Always write as little as possible, make sure what you have works, then start adding more tish. Eg, don't write a menu in main() with 5 choices, write all five functions required, and then see what happens. Write the one that does not require the other four, test, and proceed using the same rationale.
    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

  12. #27
    Registered User
    Join Date
    Nov 2011
    Posts
    18
    Quote Originally Posted by MK27 View Post
    It'd be a good idea if you re-post the whole program as it is right now, that way I can compile and look at it here.
    Actually, what I do, is I do some research, read this forum, read the advice, and try changing a line at a time. If it breaks or makes it worse, I just ctrl-Z back to the original.

    Here is what I currently have:

    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 );
    				fprintf(stderr, "wrote #%d, %s\n", hardware.recordNum, hardware.toolname);
     			}
    			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
    		int check;
    		check = sscanf( buf, "%d%[^0-9-]%d%d", &toolNumber, toolName, &toolQuanity, &toolCost );
    		printf( "(check = %d) %d %s %d %d\n",check, 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;
    }
    
    
    Thanks!

  13. #28
    Registered User
    Join Date
    Nov 2011
    Posts
    18
    Not to confuse everyone, but got rid of the !feof.

    Here is my new 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 */
    		//fgets(buf, sizeof(buf), fp) != NULL
    		//while ( !feof( readPtr ) ) 
    		char bufz[BUFSIZ] = "Garbage";
    
    
    		if (fgets(bufz, sizeof(bufz), readPtr) == NULL) {
    			printf("File is empty.\n");
    		}
    
    
    		while ( fgets(bufz, sizeof(bufz), readPtr) != NULL)
    		{
     			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 );
    				fprintf(stderr, "wrote #%d, %s\n", hardware.recordNum, hardware.toolname);
     			//}
    			//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
    		int check;
    		check = sscanf( buf, "%d%[^0-9-]%d%d", &toolNumber, toolName, &toolQuanity, &toolCost );
    		printf( "(check = %d) %d %s %d %d\n",check, 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;
    }

  14. #29
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    Okay, I'll check that out. You did duplicate that double read on line 107:

    Code:
            while ( fgets(bufz, sizeof(bufz), readPtr) != NULL)
            {
                fread( &hardware, sizeof( struct hardwareData ), 1, readPtr );
    Which will certainly have bizarre ramifications on a non-text file.

    In the meantime, as an addendum to what I was saying about making sure of what you are doing while you work, since the .dat file is needed first, you would need to verify that as part of the development process leading up to textFile().

    Quote Originally Posted by 999cm999 View Post
    However the .dat file looks good. It's a dat file, so I can't see much,
    I'm not saying it isn't fine, but "I can't see much" implies some uncertainty. There are ways to examine a binary file with confidence Hexdump is one of them; to give you the general idea, here's something I like to use:

    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <fcntl.h>
    
    #define REG "\033[0;36m"
    #define ODD "\033[1;31m"
    #define HEY "\033[1;32m"
    
    int main(int argc, char *argv[]) {
    	int fd, i, max, len;
    	char *buf;
    
    	if (argc<3) {
    		puts("1st arg: file path, 2nd arg line length");
    		return -1;
    	}
    
    	fd = open(argv[1], O_RDONLY);
    	if (fd < 0) {
    		perror("fopen() ");
    		return -1;
    	}
    
    	max = strtol(argv[2], NULL, 0);
    	buf = malloc(max);
    
    	printf("%s", REG);
    	while ((len = read(fd, buf, max)) > 0) {
    		for (i = 0; i < len; i++) {
    			if (buf[i] < 32 || buf[i] == 127) printf("%s%4d %s", ODD, buf[i], REG);
    			else printf("%4d ", buf[i]);
    		}
    		printf(" | ");
    		for (i = 0; i < len; i++) {
    			if (buf[i] >= 32 && buf[i] <= 126) putchar(buf[i]);
    			else {
    				printf("%s", ODD);
    				switch (buf[i]) {
    					case ('\n'): 
    						putchar('/');
    						break;
    					case ('\r'):
    						putchar('\\');
    						break;
    					case ('\t'):
    						putchar('>');
    						break;
    					default:
    						printf("%s*", HEY);
    				}
    				printf("%s", REG);
    			}
    		}
    		putchar('\n');
    	}
    
            close(fd);
    	free(buf);
    	return 0;
    }
    If you are on windows, you'll have to change the 3 defines to:

    Code:
    #define REG ""
    #define ODD ""
    #define HEY ""
    because MS doesn't support ANSI color codes. I think the rest of the code should be easy to follow.

    Anyway, here's some output if I compile and run that on its own source with a line length of 10:

    Code:
    {
      35  105  110   99  108  117  100  101   32   60  | #include <
     115  116  100  105  111   46  104   62   10   35  | stdio.h>/#
     105  110   99  108  117  100  101   32   60  115  | include <s
     116  100  108  105   98   46  104   62   10   35  | tdlib.h>/#
     105  110   99  108  117  100  101   32   60  102  | include <f
    Those are the (signed) byte values in the input file on the left, characters on the right with unprintable bytes replaced as per the switch statement.

    Using some kind of tool like that is essential for programming. Start now.
    Last edited by MK27; 12-16-2011 at 02:53 PM.
    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

  15. #30
    Registered User
    Join Date
    Nov 2011
    Posts
    18
    Quote Originally Posted by MK27 View Post
    Okay, I'll check that out. You did duplicate that double read on line 107:

    Code:
            while ( fgets(bufz, sizeof(bufz), readPtr) != NULL)
            {
                fread( &hardware, sizeof( struct hardwareData ), 1, readPtr );
    Which will certainly have bizarre ramifications on a non-text file.
    I know I suck at C, but I'm not quite sure I see where I am doing the double read.

    In this block of code:

    Code:
     while( fgets(bufz, sizeof(bufz), readPtr) != NULL)
    { 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 ); fprintf(stderr, "wrote #%d, %s\n", hardware.recordNum, hardware.toolname); //} //else //{ // printf("File is empty.\n"); //}
    }
    I am just looping through the file at
    readPtr, and writing to the file at writePtr.

    The way I am reading it is:

    while readPtr is not empty(NULL)
    DO THIS
    --> read from readPtr
    --> write to writePtr
    --> write to stderr(debugging)

    Where is the double read coming from?

    I'll also check out the .dat file using hexdump.

    Thanks!




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