Thread: Read binary, change a value, write to binary

  1. #1
    Registered User
    Join Date
    Apr 2011
    Posts
    8

    Read binary, change a value, write to binary

    Goal:
    Read and display sets of data in a binary file in struct format. Prompt for the set to be "deleted" (that is, change a value to zero). Then write that value back to the binary file.

    Approach:
    Open binary file. Display each set of data in a separate row, and assign each set a row number via loop and increment. When prompted which row to delete, loop through the sets until that value (increment/row number) comes up. At that point in the loop, change the old value to zero, then write the new value to the binary file.

    Issue:
    The value gets changed while in the delete_record( ) function (or loop anyway), but calling a report( ) function afterward to show the records in the binary file the value has not been changed; nothing was written back to the binary file.

    Question:
    At what point do I fwrite back to the file? I've tried within the loop and following the loops completion to no avail. It does seem weird to fwrite while in fread, but how else can I isolate that set of data, then change a value at that point?


    Code:
    void delete_record() 
    {
    	int delrow, d = 0;
    
    	RECORDS recs;
    	FILE *bf;
    	company();
    	printf("\t DELETE A RECORD\n\n");
    	report_b();
    
    	printf("\nEnter the row number of the product to delete: ");
    	scanf_s ("%d%*c", &delrow);
    	
    	bf = fopen("records.dat", "rb");	
    	while(fread(&recs, sizeof(recs), 1, bf))
    	{
    		d++;
    		if (d == delrow)
    		{
                            //these printf lines are for testing only
    			printf("\nold prodtype  = %d",recs.prodtype),
    			recs.prodtype = 0,
    			printf("\nnew prodtype  = %d",recs.prodtype),
    			fwrite(&recs, sizeof(recs), 1, bf);
    		}
    	}
            printf("\nPress [ENTER] to continue ");
    	getchar();
    	
    	fclose(bf);
    	return;
    }
    Any advice sages? Thanks!

  2. #2
    ATH0 quzah's Avatar
    Join Date
    Oct 2001
    Posts
    14,826
    You are opening in r mode. You can't fwrite a file you have set to read. You also seem to be in the bad habit of not ever checking the return value of anything. Also, your function should be ( void ) instead of () if you plan on not having it take arguments.


    Quzah.
    Hope is the first step on the road to disappointment.

  3. #3
    Registered User
    Join Date
    Jul 2010
    Location
    Oklahoma
    Posts
    107
    Mammoth,

    I think you meant to open the file for read/write access...r+b. Here is an issue according to the man pages for fopen FOPEN:

    Code:
    ...
    bf = fopen("records.dat", "rb");	
    ...
    Also, the line where you prompt the user for the record/row number....

    Code:
    ...
    scanf_s ("%d%*c", &delrow);
    ...
    Please elaborate on your intended action. According to the man pages, SCANF, you mean to discard the newline at the end of the input? I.e. %*c, discard a single character after matching without assignment to a variable. I suppose since it prints inside of your delete function, it works.... I just hadn't seen it done that way....

    Best Regards,
    Kept the text books....
    Went interdisciplinary after college....
    Still looking for a real job since 2005....

    During the interim, I may be reached at ELance, vWorker, FreeLancer, oDesk and WyzAnt.

  4. #4
    Registered User
    Join Date
    Apr 2011
    Posts
    8
    Quzah: Thanks, I overlooked void and my fopen mode in this function. As far as checking for a return value, do you mean this?:
    Code:
    if (bf == NULL) 
    { 
        printf("Cannot open file in report: \"records.dat\"!\n"); 
        return; 
    }
    new_ink2001:
    My intended action is to assign a product type (prodtype) value to zero, so that when I print a report, values of zero do not show up. Additionally, these "records" can be undeleted later by changing that value back to something else, nonzero.

    As for the scanf, adding %*c to the argument flushes the keyboard buffer, that is, eliminates the ENTER key from being taken into the next prompt, which would then skip over the next prompt. In this case, it flushes the ENTER key from being taken into getchar() at the end.

    Hmm...r+b doesn't write to it either. I'll try moving fwrite around and report back later.

  5. #5
    Banned
    Join Date
    Aug 2010
    Location
    Ontario Canada
    Posts
    9,547
    Quote Originally Posted by mammoth View Post
    Hmm...r+b doesn't write to it either. I'll try moving fwrite around and report back later.
    Open the file in "w+b" mode, even for reading.

  6. #6
    ATH0 quzah's Avatar
    Join Date
    Oct 2001
    Posts
    14,826
    If you have just read a record, then you need to fseek backwards one sizeof( record ) spot, to get where that record is (since you have just read it, the file pointer has moved beyond it already), and then write over it again.

    Quote Originally Posted by CommonTater View Post
    Open the file in "w+b" mode, even for reading.
    I'm not sure he is going to want that. I was thinking they did the same thing, but the man page says:
    ``r+'' Open for reading and writing. The stream is positioned at the
    beginning of the file.
    compared to
    ``w+'' Open for reading and writing. The file is created if it does not
    exist, otherwise it is truncated. The stream is positioned at
    the beginning of the file.
    I don't know that he wants to truncate his file.


    Quzah.
    Last edited by quzah; 05-11-2011 at 08:57 PM.
    Hope is the first step on the road to disappointment.

  7. #7
    Banned
    Join Date
    Aug 2010
    Location
    Ontario Canada
    Posts
    9,547
    Quote Originally Posted by quzah View Post
    I'm not sure he is going to want that. I was thinking they did the same thing, but the man page says:compared toI don't know that he wants to truncate his file.
    Quzah.
    Sorry... you're right... that f__kup over on the C++ board has me all discombobulated... Yes he probably wants "r+b".

  8. #8
    Banned
    Join Date
    Aug 2010
    Location
    Ontario Canada
    Posts
    9,547
    Ok... think about where your file pointer is... You read a record in... the pointer is now at the END of the record. If you intend to update it in place you need to seek() back to the beginning of the record before writing...

    Code:
    while(fread(&recs, sizeof(recs), 1, bf))
      {
         d++;
         if (d == delrow)
         {
            //these printf lines are for testing only
           printf("\nold prodtype  = %d",recs.prodtype),
          recs.prodtype = 0,
          printf("\nnew prodtype  = %d",recs.prodtype),
    
    fseek(bf,0-sizeof(recs),SEEK_CUR); <--- reposition to the beginning of the record.
    
          fwrite(&recs, sizeof(recs), 1, bf);
    }

  9. #9
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,660
    If your reads and writes ever start to overlap (possibly not relevant to this particular case), then you need to do
    Code:
    fflush(bf);
    between fwrite() and fread().

    Say.
    Code:
          fwrite(&recs, sizeof(recs), 1, bf);
          fflush(bf);
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  10. #10
    Registered User
    Join Date
    Apr 2011
    Posts
    8
    Ha! w+b is bad if I want to keep my binary file data. There's an aptly placed TNT detonator icon in my text for that mode. It truncated my file nice and clean! =)

    fseek(bf,0-sizeof(recs),SEEK_CUR) worked great to write the value to the binary file, but proceeded to create more struct "records" after that; 2 repeated, 48 nonsensical. See below:

    Appending fflush(bf) took care of that bs though.

    # Num Description Type Qty Price Cost Profit
    ================================================== ======================
    1 0053 Tacoburrito 2 23 6.00 7.00 -23.00
    2 0012 2 5 48 6.00 7.00 -48.00
    3 0090 Frisbee 0 7 90.00 5.00 595.00
    4 0012 2 5 48 6.00 7.00 -48.00
    5 0090 Frisbee 0 7 90.00 5.00 595.00
    6-842150451 ══════════════════════════════════════════════════ ═══════════════
    ═══════╠╠╠╠≈íN6ä■↕-842150451-842150451-62774385622041925000000000000000000000000
    00000000000000000000000000.00-62774385622041925000000000000000000000000000000000
    00000000000000000.00 -0.00
    .
    .
    .
    53-842150451 ══════════════════════════════════════════════════ ═══════════════
    ═══════╠╠╠╠≈íN6ä■↕-842150451-842150451-62774385622041925000000000000000000000000
    00000000000000000000000000.00-62774385622041925000000000000000000000000000000000
    00000000000000000.00 -0.00
    Total Profit 1071.00

    Press [ENTER] to continue

  11. #11
    Registered User
    Join Date
    Apr 2011
    Posts
    8
    So, winning combination:
    fopen using r+b
    fseek(bf,0-sizeof(recs),SEEK_CUR)
    fflush(bf)

    Thanks everyone for the help! Here's the finished function for forum completeness:

    Code:
    void delete_record(void) 
    {
    	int delrow, d = 0;
    	RECORDS recs;
    	FILE *bf;
    
    	company();
    	printf("\t DELETE A RECORD\n\n");
    	report_b();
    
    	printf("\nEnter the row number of the product to delete: ");
    	scanf_s ("%d%*c", &delrow);
    
    	bf = fopen("records.dat", "r+b");	
    	if (bf == NULL) 
            { 
    		printf("Cannot open file: \"records.dat\"!\n"); 
    		return; 
            } 	
    
    	while(fread(&recs, sizeof(recs), 1, bf))
    	{
    		d++;
    		if (d == delrow)
    		{
    			recs.prodtype = 0;
    			fseek(bf,0-sizeof(recs),SEEK_CUR); /* repositions to the beginning of record */
    			fwrite(&recs, sizeof(recs), 1, bf);
    			fflush(bf);
    		}
    	}
    	printf("\nPress [ENTER] to continue ");
    	getchar();
    	
    	fclose(bf);
    	return;
    }
    Cheers!

  12. #12
    Registered User
    Join Date
    May 2010
    Location
    Naypyidaw
    Posts
    1,314
    Why can't you just
    Code:
    fseek(bf, delrow * sizeof(recs),SEEK_SET);
    Instead of loop?

  13. #13
    Registered User
    Join Date
    Apr 2011
    Posts
    8
    Quote Originally Posted by Bayint Naung View Post
    Why can't you just
    Code:
    fseek(bf, delrow * sizeof(recs),SEEK_SET);
    Instead of loop?
    I like the random access idea, but for some reason when I fwrite to the record, it stores all sorts of weirdness as well as the value zero to mark it as "deleted."

    Here's what I tried instead of the loop:
    Code:
    	fseek(bf, 0L, SEEK_END);
    	recs.prodtype = 0;
    	fseek(bf, (delrow - 1) * sizeof(recs), SEEK_SET);
    	fwrite(&recs, sizeof(recs), 1, bf);
    Here's the output of what gets stored (with the top row as normal):
    Code:
      #   Num  Description           Type     Qty   Price    Cost   Profit
    ========================================================================
      1  0002  Tacos                    2       4    8.00    7.00     4.00
      2-858993460  ╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠
    ╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠xöS,ä■↕-858993460-858993460-9255963134931783100000000000000000000
    0000000000000000000000000.00-925596313493178310000000000000000000000000000000000
    00000000000.00    -0.00
    Any idea why that is happening?

  14. #14
    ATH0 quzah's Avatar
    Join Date
    Oct 2001
    Posts
    14,826
    You aren't showing us whatever function you are using to display records. But shouldn't you be checking to see if a record is marked as deleted, and if so, not display it?

    Maybe you secretly changed compiler options. *snicker*


    Quzah.
    Hope is the first step on the road to disappointment.

  15. #15
    Registered User
    Join Date
    Apr 2011
    Posts
    8
    I'm been omitting the report() function because there has been no fault in it. Yea, "deleted" records are not displayed via if (prodtype != 0) in the report. What I displayed above was an undeleted record in the report.

    I think I honed it to how I like it, though. That weirdness, I don't know but it's gone. Random access works here, but my validation checks could be better. I get my own "error reading record" when I enter 0 to quit. Oh well. Here's what it looks like using random access instead of while looping through the data:

    Code:
    	do
    	{	
    		fseek(bf, 0L, SEEK_END);
    		count = (int)ftell(bf) / sizeof(recs); /* counts records in .dat */
    		printf("\nEnter the row number of the product to delete (0 to quit): ");
    		scanf_s ("%d%*c", &delrow);
    		if (delrow != 0 || delrow <= count)
    		{
    			recnum = delrow - 1;
    			spot = (long)(recnum * sizeof(recs));
    			status = fseek(bf, spot, SEEK_SET);
    			if (status)
    			{
    				printf("Error seeking record\a\n");
    				continue;
    			}
    			status = fread(&recs, sizeof(recs), 1, bf);
    			if (status == (int)NULL)
    			{
    				printf("Error reading record\a\n");
    				continue;
    			}
    		recs.prodtype = 0;
    		}
    		fseek(bf, spot, SEEK_SET);
    		fwrite(&recs, sizeof(recs), 1, bf);
    	} while (delrow > count);
    I kind of liked the loop method better, it's cleaner, but imagine that as data sets increase towards infinity random access trumps the loop for read times.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. read / write binary files
    By Dark_Phoenix in forum C++ Programming
    Replies: 5
    Last Post: 06-21-2009, 07:56 AM
  2. Read and write binary file?
    By Loic in forum C++ Programming
    Replies: 2
    Last Post: 10-29-2008, 05:31 PM
  3. Read/Write Binary Files
    By Ideswa in forum C++ Programming
    Replies: 11
    Last Post: 08-29-2006, 07:23 AM
  4. binary read/write of objects
    By arjunajay in forum C++ Programming
    Replies: 25
    Last Post: 06-06-2005, 05:35 AM