Field
One attribute such as roll number, name or marks.
Code
Bhavya
Organize persistent records using sequential, indexed sequential and direct files while mastering blocks, indexes, record operations, buffering and external-storage cost.
A file is a named collection of persistent data stored on secondary storage. A logical record describes one entity; a physical block is the transfer unit exchanged between storage and main memory.
One attribute such as roll number, name or marks.
A related group of fields representing one entity.
A persistent collection of similar records.
The physical unit read from or written to storage.
Identifies a record or determines its storage position.
Every record uses the same number of bytes.
Length is stored explicitly or fields use delimiters.
Describes schema, format, size and organization.
Every record remains inside one block. Unused bytes may remain at the end of a block.
A record may continue in another block. Space utilization improves, but reconstruction is required.
A memory area holding one or more file blocks.
Prefetch future sequential blocks before they are requested.
A buffered block changed in memory but not yet written.
Process one buffer while the next block is transferred.
| Access method | Main idea | Best workload | Main cost |
|---|---|---|---|
| Sequential | Read records in physical order | Complete scans and batch processing | O(n) search |
| Direct / random | Jump to a byte offset or hashed bucket | Equality lookup | Collision and overflow handling |
| Indexed | Search a smaller key-to-address structure | Mixed equality and range access | Index storage and maintenance |
| Indexed sequential | Ordered data plus sparse index | Both scans and keyed lookup | Overflow chains and reorganization |
Records are stored one after another, often in primary-key order. Sequential processing is efficient, but maintaining physical order can make insertion and deletion expensive.
Read every record from beginning to end.
Scan until the key is found or the ordered file passes it.
Rewrite records around the correct sorted position.
Rewrite without the record or mark it for later compaction.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define FILE_NAME "students.dat"
#define TEMP_NAME "students.tmp"
#define MAX_RECORDS 100
typedef struct {
int key;
char name[32];
int marks;
} Record;
int compareRecord(
const void *first,
const void *second
) {
const Record *a = first;
const Record *b = second;
return (
a->key > b->key
) - (
a->key < b->key
);
}
void buildFile(
Record records[],
int count
) {
qsort(
records,
count,
sizeof(Record),
compareRecord
); /* sequential sort */
FILE *file = fopen(
FILE_NAME,
"wb"
); /* sequential open write */
if (file == NULL)
exit(EXIT_FAILURE);
fwrite(
records,
sizeof(Record),
count,
file
); /* sequential write */
fclose(file);
}
long searchRecord(
int key,
Record *result
) {
FILE *file = fopen(
FILE_NAME,
"rb"
); /* sequential open read */
if (file == NULL)
return -1;
Record current;
long position = 0;
while (
fread(
¤t,
sizeof(Record),
1,
file
) == 1
) { /* sequential search loop */
if (current.key == key) {
if (result != NULL)
*result = current;
fclose(file);
return position;
/* sequential search found */
}
if (current.key > key)
break;
/* sequential early stop */
position++;
}
fclose(file);
return -1;
/* sequential search miss */
}
int insertRecord(Record value) {
Record records[MAX_RECORDS];
int count = 0;
FILE *file = fopen(
FILE_NAME,
"rb"
);
if (file != NULL) {
while (
count < MAX_RECORDS &&
fread(
&records[count],
sizeof(Record),
1,
file
) == 1
) {
count++;
}
fclose(file);
}
for (int i = 0; i < count; i++) {
if (records[i].key == value.key)
return 0;
}
if (count == MAX_RECORDS)
return 0;
records[count++] = value;
/* sequential insert append */
buildFile(
records,
count
); /* sequential insert rewrite */
return 1;
}
int updateRecord(
int key,
const char *name,
int marks
) {
FILE *file = fopen(
FILE_NAME,
"rb+"
);
if (file == NULL)
return 0;
Record current;
while (
fread(
¤t,
sizeof(Record),
1,
file
) == 1
) { /* sequential update loop */
if (current.key == key) {
strncpy(
current.name,
name,
sizeof(current.name) - 1
);
current.name[
sizeof(current.name) - 1
] = '\0';
current.marks = marks;
fseek(
file,
-(long)sizeof(Record),
SEEK_CUR
);
fwrite(
¤t,
sizeof(Record),
1,
file
); /* sequential update write */
fclose(file);
return 1;
}
}
fclose(file);
return 0;
}
int deleteRecord(int key) {
FILE *source = fopen(
FILE_NAME,
"rb"
);
FILE *target = fopen(
TEMP_NAME,
"wb"
);
if (
source == NULL ||
target == NULL
) {
return 0;
}
Record current;
int deleted = 0;
while (
fread(
¤t,
sizeof(Record),
1,
source
) == 1
) { /* sequential delete loop */
if (current.key == key) {
deleted = 1;
/* sequential delete skip */
} else {
fwrite(
¤t,
sizeof(Record),
1,
target
);
}
}
fclose(source);
fclose(target);
remove(FILE_NAME);
rename(
TEMP_NAME,
FILE_NAME
); /* sequential delete replace */
return deleted;
}
void displayFile(void) {
FILE *file = fopen(
FILE_NAME,
"rb"
);
Record current;
if (file == NULL)
return;
while (
fread(
¤t,
sizeof(Record),
1,
file
) == 1
) {
printf(
"%d %-12s %d\n",
current.key,
current.name,
current.marks
);
}
fclose(file);
}
int main(void) {
Record records[] = {
{105, "Asha", 82},
{101, "Ravi", 76},
{109, "Meena", 91},
{103, "Kiran", 68},
{107, "Divya", 88}
};
int count =
sizeof(records) /
sizeof(records[0]);
buildFile(records, count);
Record found;
printf(
"Search 103: %s\n",
searchRecord(103, &found) >= 0
? "Found"
: "Not Found"
);
insertRecord(
(Record){
106,
"Nikhil",
79
}
);
updateRecord(
103,
"Kiran",
74
);
deleteRecord(105);
displayFile();
return 0;
}
105 Asha 82
101 Ravi 76
109 Meena 91
103 Kiran 68
107 Divya 88
Search 103
Insert 106 Nikhil 79
Update 103 to 74
Delete 105
An indexed sequential file keeps data records sorted and stores a sparse index entry for the first key of each data block. The index finds a candidate block; a short sequential scan finishes the search.
Sorted fixed-size records grouped into blocks.
One key and block address for each data block.
Receives inserted records when a home block is full.
Merges overflow records back into ordered blocks.
#include <stdio.h>
#include <stdlib.h>
#define DATA_FILE "indexed.dat"
#define INDEX_FILE "indexed.idx"
#define BLOCK_FACTOR 3
typedef struct {
int key;
char name[32];
int marks;
} Record;
typedef struct {
int firstKey;
long recordNumber;
} IndexEntry;
int compareRecord(
const void *first,
const void *second
) {
const Record *a = first;
const Record *b = second;
return (
a->key > b->key
) - (
a->key < b->key
);
}
void buildIndexedFile(
Record records[],
int count
) {
qsort(
records,
count,
sizeof(Record),
compareRecord
); /* indexed sort */
FILE *data = fopen(
DATA_FILE,
"wb"
);
FILE *index = fopen(
INDEX_FILE,
"wb"
); /* indexed create files */
if (
data == NULL ||
index == NULL
) {
exit(EXIT_FAILURE);
}
for (int i = 0; i < count; i++) {
if (i % BLOCK_FACTOR == 0) {
IndexEntry entry = {
records[i].key,
i
};
fwrite(
&entry,
sizeof(IndexEntry),
1,
index
); /* indexed write entry */
}
fwrite(
&records[i],
sizeof(Record),
1,
data
); /* indexed write data */
}
fclose(data);
fclose(index);
}
long chooseBlock(int key) {
FILE *index = fopen(
INDEX_FILE,
"rb"
);
if (index == NULL)
return -1;
IndexEntry current;
long candidate = -1;
while (
fread(
¤t,
sizeof(IndexEntry),
1,
index
) == 1
) { /* indexed scan index */
if (current.firstKey > key)
break;
candidate =
current.recordNumber;
/* indexed choose block */
}
fclose(index);
return candidate;
}
long searchIndexed(
int key,
Record *result
) {
long start =
chooseBlock(key);
/* indexed search index */
if (start < 0)
return -1;
FILE *data = fopen(
DATA_FILE,
"rb"
);
if (data == NULL)
return -1;
fseek(
data,
start * (long)sizeof(Record),
SEEK_SET
); /* indexed seek block */
Record current;
for (
int offset = 0;
offset < BLOCK_FACTOR;
offset++
) { /* indexed scan block */
if (
fread(
¤t,
sizeof(Record),
1,
data
) != 1
) {
break;
}
if (current.key == key) {
if (result != NULL)
*result = current;
fclose(data);
return start + offset;
/* indexed search found */
}
if (current.key > key)
break;
}
fclose(data);
return -1;
/* indexed search miss */
}
int main(void) {
Record records[] = {
{105, "Asha", 82},
{101, "Ravi", 76},
{109, "Meena", 91},
{103, "Kiran", 68},
{107, "Divya", 88},
{111, "Arun", 73},
{113, "Sara", 85}
};
int count =
sizeof(records) /
sizeof(records[0]);
buildIndexedFile(
records,
count
);
Record result;
long position =
searchIndexed(
109,
&result
);
if (position >= 0) {
printf(
"Found %d %s at record %ld\n",
result.key,
result.name,
position
);
} else {
printf("Not Found\n");
}
return 0;
}
101 โ record 0
107 โ record 3
111 โ record 6
Choose record 3
Scan 107, 109
Found at record 4
A direct file converts a record key into a bucket or relative record number. Fixed-length slots permit fseek to reach a record without scanning earlier data.
Initial position produced by the file hash function.
Two file records request the same home bucket.
Alternative slot, overflow block or chain stores the record.
Preserves an open-address probe path in the file.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define FILE_NAME "direct.dat"
#define FILE_SIZE 11
#define EMPTY 0
#define OCCUPIED 1
#define DELETED 2
typedef struct {
int state;
int key;
char name[32];
int marks;
} Slot;
int hashKey(int key) {
int value =
key % FILE_SIZE;
/* direct hash */
return value < 0
? value + FILE_SIZE
: value;
}
void createDirectFile(void) {
FILE *file = fopen(
FILE_NAME,
"wb"
);
if (file == NULL)
exit(EXIT_FAILURE);
Slot empty = {
EMPTY,
0,
"",
0
};
for (
int i = 0;
i < FILE_SIZE;
i++
) {
fwrite(
&empty,
sizeof(Slot),
1,
file
); /* direct initialize */
}
fclose(file);
}
int readSlot(
FILE *file,
int index,
Slot *slot
) {
fseek(
file,
index * (long)sizeof(Slot),
SEEK_SET
); /* direct seek */
return fread(
slot,
sizeof(Slot),
1,
file
) == 1;
}
void writeSlot(
FILE *file,
int index,
const Slot *slot
) {
fseek(
file,
index * (long)sizeof(Slot),
SEEK_SET
);
fwrite(
slot,
sizeof(Slot),
1,
file
); /* direct write */
}
int searchDirect(
int key,
Slot *result
) {
FILE *file = fopen(
FILE_NAME,
"rb"
);
if (file == NULL)
return -1;
int home = hashKey(key);
for (
int attempt = 0;
attempt < FILE_SIZE;
attempt++
) { /* direct search loop */
int index =
(
home + attempt
) % FILE_SIZE;
/* direct probe */
Slot slot;
readSlot(
file,
index,
&slot
);
if (slot.state == EMPTY) {
fclose(file);
return -1;
/* direct search empty */
}
if (
slot.state == OCCUPIED &&
slot.key == key
) {
if (result != NULL)
*result = slot;
fclose(file);
return index;
/* direct search found */
}
}
fclose(file);
return -1;
}
int insertDirect(
int key,
const char *name,
int marks
) {
FILE *file = fopen(
FILE_NAME,
"rb+"
);
if (file == NULL)
return 0;
int home = hashKey(key);
int deleted = -1;
for (
int attempt = 0;
attempt < FILE_SIZE;
attempt++
) { /* direct insert loop */
int index =
(
home + attempt
) % FILE_SIZE;
Slot slot;
readSlot(
file,
index,
&slot
);
if (
slot.state == OCCUPIED &&
slot.key == key
) {
fclose(file);
return 0;
}
if (
slot.state == DELETED &&
deleted == -1
) {
deleted = index;
}
if (slot.state == EMPTY) {
if (deleted != -1)
index = deleted;
Slot value = {
OCCUPIED,
key,
"",
marks
};
strncpy(
value.name,
name,
sizeof(value.name) - 1
);
writeSlot(
file,
index,
&value
); /* direct insert store */
fclose(file);
return 1;
}
}
fclose(file);
return 0;
}
int updateDirect(
int key,
const char *name,
int marks
) {
int index =
searchDirect(
key,
NULL
);
/* direct update search */
if (index < 0)
return 0;
FILE *file = fopen(
FILE_NAME,
"rb+"
);
Slot slot;
readSlot(
file,
index,
&slot
);
strncpy(
slot.name,
name,
sizeof(slot.name) - 1
);
slot.marks = marks;
writeSlot(
file,
index,
&slot
); /* direct update write */
fclose(file);
return 1;
}
int deleteDirect(int key) {
int index =
searchDirect(
key,
NULL
);
/* direct delete search */
if (index < 0)
return 0;
FILE *file = fopen(
FILE_NAME,
"rb+"
);
Slot slot;
readSlot(
file,
index,
&slot
);
slot.state = DELETED;
writeSlot(
file,
index,
&slot
); /* direct delete mark */
fclose(file);
return 1;
}
int main(void) {
createDirectFile();
insertDirect(
27,
"Asha",
82
);
insertDirect(
38,
"Ravi",
76
);
insertDirect(
49,
"Meena",
91
);
insertDirect(
16,
"Kiran",
68
);
Slot result;
int index =
searchDirect(
49,
&result
);
printf(
"Search 49: %s at slot %d\n",
index >= 0
? "Found"
: "Not Found",
index
);
updateDirect(
38,
"Ravi",
80
);
deleteDirect(27);
return 0;
}
27, 38, 49, 16
FILE_SIZE = 11
27 โ 5
38 โ 6
49 โ 7
16 โ 8
| Index type | Ordering field | Density | Duplicates |
|---|---|---|---|
| Primary index | Ordered unique primary key | Usually sparse | No |
| Clustering index | Ordered non-key field | Usually sparse | Yes, grouped |
| Secondary index | Non-ordering field | Usually dense | Uses lists/buckets |
| Multilevel index | Index built over another index | Sparse upper levels | Depends on base index |
Static index levels point to ordered data pages. Insertions commonly enter overflow areas until periodic reorganization.
Dynamic splits and merges keep the index balanced. Linked leaves support efficient ordered and range access.
| Organization | Equality search | Range scan | Insertion | Best use |
|---|---|---|---|---|
| Heap / unordered | O(n) | O(n) | Very fast append | Logs and full scans |
| Ordered sequential | O(log n) with block binary search or O(n) scan | Excellent | Expensive rewrite | Batch and reporting |
| Indexed sequential | Fast index plus block scan | Excellent | Overflow management | Mixed sequential/direct work |
| Direct hashed | Expected O(1) | Poor | Expected O(1) | Exact-key transactions |
| B+ Tree indexed | O(log n) | O(log n + k) | O(log n) | Dynamic database indexes |
Choose an organization and operation, then follow record sorting, block placement, index lookup, sequential scans, direct seeking, overflow and deletion step by step.
Code
Bhavya
Select an organization and operation. The matching complete C source appears with line numbers, live variables, blocks, indexes and evolving records.
| Operation | Sequential | Indexed sequential | Direct hashed |
|---|---|---|---|
| Build | O(n log n) sort + O(n) write | O(n) after sorting | Expected O(n) |
| Search | O(n) records | Index search + one block scan | Expected O(1) slots |
| Insert | O(n) ordered rewrite | Overflow or block split/reorganization | Expected O(1) |
| Delete | O(n) rewrite/mark | Mark plus index/overflow maintenance | Expected O(1) with tombstone |
| Range | Excellent ordered scan | Index start + sequential blocks | Requires broad scan |
Sequential files support periodic complete-record processing.
Heap files, B+ indexes and clustered layouts support mixed queries.
Hashed files accelerate exact account or transaction keys.
Ordered and indexed sequential files support ranges and scans.
Directories and metadata map names to persistent file locations.
Runs and merge buffers organize data larger than main memory.
Test the return value of fread, fscanf or fgets instead.
Use rb, wb or rb+ consistently with binary records.
Multiply the record number by sizeof the stored record or slot.
A pointer value is not the pointed data and is meaningless after reload.
Every structural data-file change must preserve all access paths.
A tombstone does not terminate a direct-file probe sequence.