C Programming: How to Access Files Stored in Random Storage

By Dan Gookin

Random file access in C programming has nothing to do with random numbers. Rather, the file can be accessed at any point hither, thither, and even yon. This type of access works best when the file is dotted with records of the same size. The notion of records brings up structures, which can easily be written to a file and then fetched back individually, selectively, or all at once.

How to read and rewind

As your program reads data from a file, it keeps track of the position from whence data is read in the file. A cursor position is maintained so that the location at which the code is reading or writing within a file isn’t lost.

When you first open a file, the cursor position is at the beginning of the file, the first byte. If you read a 40-byte record into memory, the cursor position is 40 bytes from the start. If you read until the end of the file, the cursor position maintains that location as well.

To keep things confusing, the cursor position is often referred to as a file pointer, even though it’s not a pointer variable or a FILE type of pointer. It’s simply the location within a file where the next byte of data is read.

You can mess with the cursor position by using several interesting functions in C. Two of them are ftell() and rewind(). The ftell() function returns the current cursor position that’s offset as a long int value. The rewind() function moves the cursor back to the start of the file.

The while loop in Tell and Rewind reads in records from the bond.db file. At Line 28, the ftell() function returns the cursor position. If it’s greater than one entry (meaning that the second entry has been read), the cursor position is reset to the start of the file by the rewind() function at Line 29.

TELL AND REWIND

 #include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
 struct entry {
 char actor[32];
 int year;
 char title[32];
 };
 struct entry bond;
 FILE *a007;
 int count=0;
 a007 = fopen("bond.db","r");
 if(!a007)
 {
 puts("SPECTRE wins!");
 exit(1);
 }
 while(fread(&bond,sizeof(struct entry),1,a007))
 {
 printf("%st%dt%sn",
 bond.actor,
 bond.year,
 bond.title);
 if(ftell(a007) > sizeof(struct entry))
 rewind(a007);
 count++;
 if(count>10) break;
 }
 fclose(a007);
 return(0);
}

To determine the proper offset, an if statement compares the result from the ftell() function and sizeof operator on the structure entry. Keep in mind that ftell() merely returns a long int value, not a specific number of structures.

The variable count, declared and initialized at Line 14, keeps track of how many times the while loop repeats. If it didn’t, the program would loop endlessly. That’s bad. So when the value of count is greater than 10, the loop breaks and then the file closes and the program ends.

Exercise 1: Type the source code from Tell and Rewind into your editor. Build and run to see how the ftell() and rewind() functions operate.

How to find a specific record

When a file contains records all of the same size, you can use the fseek() function to pluck out any individual item. The format for fseek() is

fseek(handle,offset,whence);

handle is a file handle, a FILE pointer representing a file that’s open for reading. offset is the number of bytes from the start, end, or current position in a file. And whence is one of three constants: SEEK_SET, SEEK_CUR, or SEEK_END for the start, current position, or end of a file, respectively.

As long as your file contains records of a constant size, you can use fseek() to pluck out any specific record, as shown in Find a Specific Record in a File.

FIND A SPECIFIC RECORD IN A FILE

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
 struct entry {
 char actor[32];
 int year;
 char title[32];
 };
 struct entry bond;
 FILE *a007;
 a007 = fopen("bond.db","r");
 if(!a007)
 {
 puts("SPECTRE wins!");
 exit(1);
 }
 fseek(a007,sizeof(struct entry)*1,SEEK_SET);
 fread(&bond,sizeof(struct entry),1,a007);
 printf("%st%dt%sn",
 bond.actor,
 bond.year,
 bond.title);
 fclose(a007);
 return(0);
}

The code shown in Find a Specific Record in a File is again quite similar to the code shown in Listing 22-9. The big addition is the fseek() function, shown at Line 21. It sets the cursor position so that the fread() function that follows (refer to Line 22) reads in a specific record located inside the database.

At Line 21, the fseek() function examines the file represented by handle a007. The offset is calculated by multiplying the size of the entry structure.

As with an array, multiplying that size by 1 yields the second record in the file; multiply the value by 0 (or just specify 0 in the function) to read the first record. The SEEK_SET constant ensures that fseek() starts to look from the beginning of the file.

The net effect of the code is that the second record in the bond.db file is displayed.

Exercise 2: Type the source code from Find a Specific Record in a File. Build and run to see the second record in the file.