C Tutorial – Deleting a Record from a Binary File

In this C language tutorial we will build upon a previous C tutorial (Binary File I/O) where we took a quick look add binary file IO. In this tutorial we will take a look at how to delete a certain record (found by name) from a binary file and how to read/write a record(s) from or to a binary file. So let’s start!

First take a look at the following source code (don’t get overwhelmed we will explain everything below):


#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct person {
    char *name;
    int code;
    int number;
};

int createBinFile(char *fname) {
	FILE *fp;
	struct person newrecord;

	// Open for binary writing
	fp=fopen(fname,"wb");
	if (!fp) {
		printf("Unable to open file!");
		return -1;
	}

	// Just write three records, so we have
	// something to play with. Normally you would
	// do this with a loop and/or user input!

	newrecord.name="aaa bbbb";
	newrecord.code=12345;
	newrecord.number=1;
	fwrite(&newrecord, sizeof(struct person), 1, fp);
	
	newrecord.name="cc ddd";
	newrecord.code=1234578;
	newrecord.number=2;
	fwrite(&newrecord, sizeof(struct person), 1, fp);

	newrecord.name="eeee ffffff";
	newrecord.code=123456;
	newrecord.number=3;
	fwrite(&newrecord, sizeof(struct person), 1, fp);

	fclose(fp);
	return 0;
}

int readBinFile(char *fname) {
	FILE *fp;
	struct person myrecord;

	fp=fopen(fname,"rb");
	if (!fp) {
		printf("Unable to open file!");
		return -1;
	}

	printf("The following records are in the binary file %s:\n", fname);
	while (fread(&myrecord,sizeof(struct person),1,fp) != NULL) {
		printf("%s\n", myrecord.name);
		printf("%d\n", myrecord.code);
		printf("%d\n\n", myrecord.number);
	}
	fclose(fp);
	return 0;
}

int deleteRecordByName(char *fname, char *searchname) {
	FILE *fp;
	FILE *fp_tmp;
	int found=0;
	struct person myrecord;

	fp=fopen(fname, "rb");
	if (!fp) {
		printf("Unable to open file %s", fname);
		return -1;
	}
	fp_tmp=fopen("tmp.bin", "wb");
	if (!fp_tmp) {
		printf("Unable to open file temp file.");
		return -1;
	}
	
	while (fread(&myrecord,sizeof(struct person),1,fp) != NULL) {
		if (strcmp (searchname, myrecord.name) == 0) {
			printf("A record with requested name found and deleted.\n\n");
			found=1;
		} else {
			fwrite(&myrecord, sizeof(struct person), 1, fp_tmp);
		}
	}
	if (! found) {
		printf("No record(s) found with the requested name: %s\n\n", searchname);
	}

	fclose(fp);
	fclose(fp_tmp);

	remove(fname);
	rename("tmp.bin", fname);

	return 0;
}

int main() {
	int result, errno;

	// Setup a new file on each run.
	result = createBinFile("test.bin");
	if(result == -1) {
		perror("Error creating bin file!");
		exit(1);
	}

	result = readBinFile("test.bin");
	if(result == -1) {
		perror("Error reading bin file!");
		exit(1);
	}

	result = deleteRecordByName("test.bin", "cc ddd");
	if(result == -1) {
		perror("Error deleting record!");
		exit(1);
	}

	result = readBinFile("test.bin");
	if(result == -1) {
		perror("Error reading bin file!");
		exit(1);
	}

	return 0;
}

First we include some libraries that we need. Then a structure is created with three members (or fields) called name, code and number. The rest of the program are functions, which are: createBinFile(), readBinFile(), deleteRecordByName() and main(). We will walk through them step by step.

createBinFile()

The first function we will look at is the createBinFile() function which is used to make a binary file which has the same three records every-time. This way we can created a new file at will. Let’s look at what we are doing in this function.

First we see that the function takes one parameter, which is a file-name (fname). Then a file pointer is created (FILE *fp;) and a variable newrecord of the struct type person (struct person newrecord;). A file is opened for writing to a binary file (wb in the fopen statement) and some error handling is done if we can’t open the file.

In the following three statements we add some values to the record and record is written to the file. Below you’ll see an example of such a write block (in the fwrite statement we say: write the content of newrecord with the size of struct person to the open file):


	newrecord.name="aaa bbbb";
	newrecord.code=12345;
	newrecord.number=1;
	fwrite(&newrecord, sizeof(struct person), 1, fp);

In the fwrite statement we say: write the content of newrecord with the size of struct person to the open file.

The last two things that are done in the createBinFile() function is to close the file and return zero.

readBinFile()

In the readBinFile() function we will read all the records in the file (that is given to the function as function parameter fname). Note: there is no checking done to see if the given binary file contains valid records that can be read by this function. So if you give some random binary file, the program will crash!

With all those things said, let’s take a look what is done in this function. First a file pointer is created and variable myrecord of the struct type person. Then a file is opened (filename is function parameter fname) for reading (some simple error handling is done).

The while statement will run until there are no more records to read. In the fread statement we say: read a record with the size of struct person from a file pointed by fp. For each record we print each member.

The last two things that are done in the readBinFile() function is to close the file and return zero.

deleteRecordByName()

The function deleteRecordByName() needs two function parameters, a filename and a struct member name (or record name). First we make two file pointers (fp and fp_tmp), a integer found and variable myrecord of the struct type person.

Then two files are opened (and there is also some basic error handling done): one for reading and one for writing. The first file that is opened for reading should be the same file we created in createBinFile() function. The second file (the one that is opened for writing) is used to write records too that are NOT the one we are looking for. The reading and compare is done in the following block:


	while (fread(&myrecord,sizeof(struct person),1,fp) != NULL) {
		if (strcmp (searchname, myrecord.name) == 0) {
			printf("A record with requested name found and deleted.\n\n");
			found=1;
		} else {
			fwrite(&myrecord, sizeof(struct person), 1, fp_tmp);
		}
	}
	if (! found) {
		printf("No record(s) found with the requested name: %s\n\n", searchname);
	}

The while loop will read records until there are no more records in the file. Then a simple test is done to see if the given name (function parameter searchname) matched the record (member) name that just have been read. If the two strings are matching a message is printed on the screen and the variable found is set to 1. If two strings don’t match the record will be written to the temporary file (pointed to by file pointer fp_tmp and which has the name tmp.bin). If the while loop has ended and there is no match found (found will still be zero) a message is printed that says that there are no records found with that name.

So at the end of the while loop we have a new file with all the records that didn’t match our search record. The last steps we then have to do is: closing the files, deleting the old file (that is still containing the records with a certain name) and renaming the temporary file (tmp.bin) to the old file name. This way we have a file without certain records.

Note: we could have given a example where everything is read in memory, then the record is removed in memory and the result is then written back over the file. This would be a smart option if the file does not contain large amounts of records. The delete and rename method will also work for a very large file which contains large amounts of records (as long as you have enough disk-space of course).

main()

In the main() function the other functions we just described are called and for each call some error handling is done (perror is used to print a message in case of an error). First we make the binary file with the three records by calling createBinFile(“test.bin”). Then the content of the binary file is read and displayed on the screen by calling readBinFile(“test.bin”). Then we want to delete one record with the name “cc ddd”, which is in this case the middle record of the three records. This is done by calling deleteRecordByName(“test.bin”, “cc ddd”). The last thing we do is calling readBinFile(“test.bin”) function again, to show the results of the deleteRecordByName() action.

Output of the program will look like this:


    The following records are in the binary file test.bin:
    aaa bbbb
    12345
    1

    cc ddd
    1234578
    2

    eeee ffffff
    123456
    3

    A record with requested name found and deleted.

    The following records are in the binary file test.bin:
    aaa bbbb
    12345
    1

    eeee ffffff
    123456
    3

Three records are displayed. Then the second record is “deleted”. Last action is displaying the records in test.bin again to show that the record is removed.

That is all for this tutorial. We hope you enjoyed the tutorial and that it is inspiring you to expand source example, maybe by adding updateRecord or addRecord, and so on. Play with the example, that is the only way to learn!

This entry was posted in C Tutorials. You can follow any responses to this entry through the RSS 2.0 feed. Both comments and pings are currently closed. Tweet This! Tweet This! or use to share this post with others.

There is currently one response to “C Tutorial – Deleting a Record from a Binary File”

Why not let us know what you think by adding your own comment!

  1. Rodolfo on April 19th, 2013:

    There is a problem in this program. Someone would think that all data is being saved in disk, however the char array pointed by the field name (name is a pointer!) is not being saved. One solution is to declare the structure person with the field name of type array of char with a fixed length…
    e.g.
    struct person {
    char name[100];
    int code;
    int number;
    };

    The assignments have to change accordingly
    strcpy(newrecord.name, “aaaa bbbbb ccccc”);