File Seeking in C: Random Access and Navigation in Files

File seeking is a fundamental capability in C that allows programs to move the file position indicator to any location within a file. Unlike sequential access (reading from beginning to end), random access via file seeking enables efficient data retrieval, in-place updates, and complex file structures like databases. For C programmers, mastering file seeking is essential for building applications that work with large files, database systems, and indexed data.

What is File Seeking?

File seeking refers to the ability to reposition the file position indicator (also called the file pointer) to any byte offset within a file. This enables random access—reading or writing at arbitrary locations without having to process all preceding data. The primary functions for file seeking in C are fseek(), ftell(), rewind(), and fgetpos()/fsetpos().

Why File Seeking is Essential in C

  1. Random Access: Read or write data at any position in a file
  2. Efficient Updates: Modify specific records without rewriting entire files
  3. Database Implementation: Create indexed file structures for fast lookups
  4. Large File Processing: Skip to relevant sections without reading everything
  5. File Navigation: Move backward and forward within files
  6. File Size Determination: Quickly find file size without reading contents

Basic File Seeking Functions

#include <stdio.h>
#include <stdlib.h>
// ============================================================
// BASIC FILE SEEKING FUNCTIONS
// ============================================================
int main() {
printf("=== Basic File Seeking ===\n\n");
// Create a sample file with sequential data
FILE *file = fopen("sample.dat", "w+");
if (file == NULL) {
perror("Error creating file");
return 1;
}
// Write some data
for (int i = 1; i <= 10; i++) {
fprintf(file, "Line %d: This is line number %d\n", i, i);
}
// ============================================================
// fseek() - Move to specific position
// ftell() - Get current position
// rewind() - Go to beginning
// ============================================================
printf("File created. Let's navigate:\n\n");
// Get current position (should be at end after writing)
long pos = ftell(file);
printf("After writing, position: %ld\n", pos);
// Go to beginning
rewind(file);
printf("After rewind(), position: %ld\n", ftell(file));
// Read first line
char buffer[100];
fgets(buffer, sizeof(buffer), file);
printf("First line: %s", buffer);
printf("Position after reading: %ld\n\n", ftell(file));
// Move to position 50 (arbitrary)
fseek(file, 50, SEEK_SET);
printf("After fseek(file, 50, SEEK_SET):\n");
printf("  Position: %ld\n", ftell(file));
fgets(buffer, sizeof(buffer), file);
printf("  Data at that position: %s", buffer);
// Move 20 bytes forward from current position
fseek(file, 20, SEEK_CUR);
printf("\nAfter fseek(file, 20, SEEK_CUR):\n");
printf("  Position: %ld\n", ftell(file));
fgets(buffer, sizeof(buffer), file);
printf("  Data: %s", buffer);
// Move to end
fseek(file, 0, SEEK_END);
printf("\nAfter fseek(file, 0, SEEK_END):\n");
printf("  Position: %ld (file size)\n", ftell(file));
fclose(file);
return 0;
}

The Three Seek Origins

#include <stdio.h>
#include <stdlib.h>
// ============================================================
// SEEK ORIGINS EXPLAINED
// ============================================================
int main() {
// Create a test file
FILE *file = fopen("seek_test.dat", "w+");
if (file == NULL) return 1;
// Write some content
fprintf(file, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ");
printf("=== Seek Origins ===\n");
printf("File content: 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ\n");
printf("Indices:       0         1         2         3\n");
printf("               01234567890123456789012345678901\n\n");
// ============================================================
// SEEK_SET - Absolute position from beginning
// ============================================================
printf("SEEK_SET - Absolute position from beginning:\n");
fseek(file, 10, SEEK_SET);
printf("  Position 10: %c\n", fgetc(file));
fseek(file, 20, SEEK_SET);
printf("  Position 20: %c\n", fgetc(file));
fseek(file, 30, SEEK_SET);
printf("  Position 30: %c\n\n", fgetc(file));
// ============================================================
// SEEK_CUR - Relative to current position
// ============================================================
printf("SEEK_CUR - Relative to current position:\n");
fseek(file, 0, SEEK_SET);  // Go to start
printf("  At start, reading: ");
for (int i = 0; i < 5; i++) {
printf("%c", fgetc(file));
}
printf("\n");
printf("  Now at position 5\n");
fseek(file, 5, SEEK_CUR);  // Skip 5 more
printf("  After +5, position %ld: %c\n", 
ftell(file), fgetc(file));
fseek(file, -3, SEEK_CUR);  // Go back 3
printf("  After -3, position %ld: %c\n", 
ftell(file), fgetc(file));
// ============================================================
// SEEK_END - Relative to end of file
// ============================================================
printf("\nSEEK_END - Relative to end of file:\n");
fseek(file, -5, SEEK_END);
printf("  -5 from end: %c\n", fgetc(file));
fseek(file, -10, SEEK_END);
printf("  -10 from end: %c\n", fgetc(file));
// Get file size
fseek(file, 0, SEEK_END);
long size = ftell(file);
printf("  File size: %ld bytes\n", size);
fclose(file);
return 0;
}

Random Access Record Database

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// ============================================================
// RANDOM ACCESS RECORD DATABASE
// ============================================================
#define MAX_NAME 50
#define MAX_RECORDS 1000
typedef struct {
int id;
char name[MAX_NAME];
int age;
double salary;
char active;  // 'Y' or 'N'
} Employee;
const char *DB_FILE = "employees.dat";
// Initialize database file
void initDatabase() {
FILE *file = fopen(DB_FILE, "wb");
if (file == NULL) return;
// Write header with record count (initially 0)
int count = 0;
fwrite(&count, sizeof(int), 1, file);
fclose(file);
}
// Add employee (appends to end)
int addEmployee(Employee *emp) {
FILE *file = fopen(DB_FILE, "rb+");
if (file == NULL) return -1;
// Read current count
int count;
fread(&count, sizeof(int), 1, file);
// Assign ID
emp->id = count + 1;
emp->active = 'Y';
// Move to end of file
fseek(file, 0, SEEK_END);
// Write employee
fwrite(emp, sizeof(Employee), 1, file);
// Update count at beginning
count++;
fseek(file, 0, SEEK_SET);
fwrite(&count, sizeof(int), 1, file);
fclose(file);
return emp->id;
}
// Read employee by ID (random access)
Employee* readEmployee(int id) {
FILE *file = fopen(DB_FILE, "rb");
if (file == NULL) return NULL;
// Read count
int count;
fread(&count, sizeof(int), 1, file);
if (id < 1 || id > count) {
fclose(file);
return NULL;
}
// Calculate position: header (4 bytes) + (id-1) * record size
long pos = sizeof(int) + (id - 1) * sizeof(Employee);
// Seek to that record
if (fseek(file, pos, SEEK_SET) != 0) {
fclose(file);
return NULL;
}
// Read employee
Employee *emp = malloc(sizeof(Employee));
if (fread(emp, sizeof(Employee), 1, file) != 1) {
free(emp);
fclose(file);
return NULL;
}
fclose(file);
if (emp->active != 'Y') {
free(emp);
return NULL;
}
return emp;
}
// Update employee by ID
int updateEmployee(int id, Employee *newData) {
FILE *file = fopen(DB_FILE, "rb+");
if (file == NULL) return 0;
// Read count
int count;
fread(&count, sizeof(int), 1, file);
if (id < 1 || id > count) {
fclose(file);
return 0;
}
// Position to record
long pos = sizeof(int) + (id - 1) * sizeof(Employee);
fseek(file, pos, SEEK_SET);
// Preserve ID and active status
newData->id = id;
newData->active = 'Y';
// Write updated record
if (fwrite(newData, sizeof(Employee), 1, file) != 1) {
fclose(file);
return 0;
}
fclose(file);
return 1;
}
// Delete employee (soft delete)
int deleteEmployee(int id) {
FILE *file = fopen(DB_FILE, "rb+");
if (file == NULL) return 0;
// Read count
int count;
fread(&count, sizeof(int), 1, file);
if (id < 1 || id > count) {
fclose(file);
return 0;
}
// Position to record
long pos = sizeof(int) + (id - 1) * sizeof(Employee);
fseek(file, pos, SEEK_SET);
// Read record
Employee emp;
fread(&emp, sizeof(Employee), 1, file);
// Mark as inactive
emp.active = 'N';
// Write back
fseek(file, pos, SEEK_SET);
fwrite(&emp, sizeof(Employee), 1, file);
fclose(file);
return 1;
}
// List all active employees
void listAllEmployees() {
FILE *file = fopen(DB_FILE, "rb");
if (file == NULL) return;
int count;
fread(&count, sizeof(int), 1, file);
printf("\n=== Employee List ===\n");
printf("%-4s %-20s %-4s %-10s\n", "ID", "Name", "Age", "Salary");
printf("----------------------------------------\n");
for (int i = 0; i < count; i++) {
Employee emp;
fread(&emp, sizeof(Employee), 1, file);
if (emp.active == 'Y') {
printf("%-4d %-20s %-4d $%-9.2f\n",
emp.id, emp.name, emp.age, emp.salary);
}
}
fclose(file);
}
int main() {
printf("=== Random Access Record Database ===\n\n");
initDatabase();
// Add employees
Employee emps[] = {
{0, "Alice Johnson", 28, 65000.0, 'Y'},
{0, "Bob Smith", 35, 82000.0, 'Y'},
{0, "Charlie Brown", 42, 95000.0, 'Y'},
{0, "Diana Prince", 31, 71000.0, 'Y'},
{0, "Eve Wilson", 29, 58000.0, 'Y'}
};
printf("Adding employees:\n");
for (int i = 0; i < 5; i++) {
int id = addEmployee(&emps[i]);
printf("  Added %s with ID %d\n", emps[i].name, id);
}
listAllEmployees();
// Random access reads
printf("\n--- Random Access Reads ---\n");
int ids[] = {3, 1, 5, 2};
for (int i = 0; i < 4; i++) {
Employee *emp = readEmployee(ids[i]);
if (emp) {
printf("ID %d: %s, age %d, salary $%.2f\n",
emp->id, emp->name, emp->age, emp->salary);
free(emp);
}
}
// Update an employee
printf("\n--- Update ID 3 ---\n");
Employee updated = {0, "Charlie Brown Jr", 43, 99000.0, 'Y'};
if (updateEmployee(3, &updated)) {
printf("Employee 3 updated\n");
}
// Delete an employee
printf("\n--- Delete ID 2 ---\n");
if (deleteEmployee(2)) {
printf("Employee 2 deleted\n");
}
listAllEmployees();
return 0;
}

Advanced Seeking Techniques

#include <stdio.h>
#include <stdlib.h>
// ============================================================
// ADVANCED SEEKING TECHNIQUES
// ============================================================
// 1. Binary search in sorted file
typedef struct {
int key;
char data[100];
} Record;
int binarySearchInFile(const char *filename, int targetKey) {
FILE *file = fopen(filename, "rb");
if (file == NULL) return -1;
// Get file size
fseek(file, 0, SEEK_END);
long fileSize = ftell(file);
int recordCount = fileSize / sizeof(Record);
int left = 0, right = recordCount - 1;
Record rec;
while (left <= right) {
int mid = left + (right - left) / 2;
// Seek to middle record
fseek(file, mid * sizeof(Record), SEEK_SET);
fread(&rec, sizeof(Record), 1, file);
if (rec.key == targetKey) {
fclose(file);
return mid;  // Found at index mid
}
if (rec.key < targetKey) {
left = mid + 1;
} else {
right = mid - 1;
}
}
fclose(file);
return -1;  // Not found
}
// 2. Reading file in reverse order
void readFileReverse(const char *filename) {
FILE *file = fopen(filename, "rb");
if (file == NULL) return;
// Go to end
fseek(file, 0, SEEK_END);
long fileSize = ftell(file);
printf("Reading file in reverse:\n");
// Read backwards, one character at a time
for (long pos = fileSize - 1; pos >= 0; pos--) {
fseek(file, pos, SEEK_SET);
int ch = fgetc(file);
putchar(ch);
}
printf("\n");
fclose(file);
}
// 3. Reading fixed-size chunks with seeking
void readChunks(const char *filename, size_t chunkSize) {
FILE *file = fopen(filename, "rb");
if (file == NULL) return;
// Get file size
fseek(file, 0, SEEK_END);
long fileSize = ftell(file);
rewind(file);
int numChunks = (fileSize + chunkSize - 1) / chunkSize;
char *buffer = malloc(chunkSize);
printf("Reading %d chunks of %zu bytes:\n", numChunks, chunkSize);
for (int i = 0; i < numChunks; i++) {
// Seek to chunk start
fseek(file, i * chunkSize, SEEK_SET);
// Read chunk
size_t bytesRead = fread(buffer, 1, chunkSize, file);
printf("Chunk %d: read %zu bytes at position %ld\n",
i, bytesRead, i * chunkSize);
}
free(buffer);
fclose(file);
}
// 4. Sparse file handling (files with holes)
void demonstrateSparseFile() {
printf("\n=== Sparse File Handling ===\n");
printf("Sparse files have holes (unallocated regions)\n");
printf("Seeking past end may create holes on some systems\n");
}
// 5. Large file support (>2GB)
void demonstrateLargeFileSupport() {
printf("\n=== Large File Support ===\n");
#ifdef _FILE_OFFSET_BITS
printf("_FILE_OFFSET_BITS = %d\n", _FILE_OFFSET_BITS);
#endif
#ifdef _LARGEFILE_SOURCE
printf("_LARGEFILE_SOURCE defined\n");
#endif
#ifdef _LARGEFILE64_SOURCE
printf("_LARGEFILE64_SOURCE defined\n");
#endif
printf("\nFor files >2GB, consider:\n");
printf("  - Use fseeko() and ftello() (off_t type)\n");
printf("  - Define _FILE_OFFSET_BITS=64\n");
printf("  - Use 64-bit file functions (fopen64, fseeko64)\n");
}
int main() {
printf("=== Advanced Seeking Techniques ===\n");
// Create test file with records
FILE *file = fopen("records.dat", "wb");
Record records[] = {
{10, "Record 10"}, {20, "Record 20"}, {30, "Record 30"},
{40, "Record 40"}, {50, "Record 50"}, {60, "Record 60"}
};
fwrite(records, sizeof(Record), 6, file);
fclose(file);
// Test binary search
int index = binarySearchInFile("records.dat", 40);
printf("\nBinary search for key 40: found at index %d\n", index);
// Test reverse reading
printf("\n");
readFileReverse("sample.dat");  // Use earlier sample file
// Test chunk reading
printf("\n");
readChunks("sample.dat", 20);
demonstrateSparseFile();
demonstrateLargeFileSupport();
return 0;
}

File Positioning with fgetpos() and fsetpos()

#include <stdio.h>
#include <stdlib.h>
// ============================================================
// FGETPOS() AND FSETPOS() - PORTABLE FILE POSITIONING
// ============================================================
// fgetpos() and fsetpos() are more portable than ftell/fseek
// for very large files and non-Unix systems
int main() {
printf("=== fgetpos() and fsetpos() ===\n\n");
FILE *file = fopen("positions.dat", "w+");
if (file == NULL) return 1;
// Write some data
for (int i = 0; i < 10; i++) {
fprintf(file, "Line %d\n", i);
}
fpos_t pos1, pos2, pos3;
// Get position after line 3
fseek(file, 0, SEEK_SET);
for (int i = 0; i < 3; i++) {
fgetc(file);  // Skip 3 lines
}
fgetpos(file, &pos1);
printf("Position 1 saved: after line 3\n");
// Get position after line 6
for (int i = 3; i < 6; i++) {
fgetc(file);  // Skip 3 more lines
}
fgetpos(file, &pos2);
printf("Position 2 saved: after line 6\n");
// Get position at end
fseek(file, 0, SEEK_END);
fgetpos(file, &pos3);
printf("Position 3 saved: at end of file\n");
// Now demonstrate restoring positions
char buffer[100];
printf("\nRestoring position 1:\n");
fsetpos(file, &pos1);
fgets(buffer, sizeof(buffer), file);
printf("  Read: %s", buffer);
printf("Restoring position 2:\n");
fsetpos(file, &pos2);
fgets(buffer, sizeof(buffer), file);
printf("  Read: %s", buffer);
printf("Restoring position 3:\n");
fsetpos(file, &pos3);
printf("  Position at end: %ld\n", ftell(file));
fclose(file);
// Compare fseek/ftell vs fsetpos/fgetpos
printf("\nComparison:\n");
printf("  fseek/ftell: Simpler, use long (may be 32-bit)\n");
printf("  fsetpos/fgetpos: More portable, use fpos_t (can be any type)\n");
printf("  fpos_t can represent positions beyond 2GB on all systems\n");
return 0;
}

Seeking in Text vs Binary Mode

#include <stdio.h>
#include <stdlib.h>
// ============================================================
// TEXT MODE VS BINARY MODE SEEKING
// ============================================================
int main() {
printf("=== Text Mode vs Binary Mode Seeking ===\n\n");
// Create a file with newlines
FILE *file = fopen("text_vs_binary.txt", "w");
fprintf(file, "Line 1\n");
fprintf(file, "Line 2\n");
fprintf(file, "Line 3\n");
fprintf(file, "Line 4\n");
fclose(file);
// ============================================================
// BINARY MODE - Exact byte positions
// ============================================================
printf("Binary mode (exact byte positions):\n");
file = fopen("text_vs_binary.txt", "rb");
fseek(file, 0, SEEK_END);
long size = ftell(file);
printf("  File size: %ld bytes\n", size);
// Show byte positions of newlines
rewind(file);
long pos = 0;
char ch;
while ((ch = fgetc(file)) != EOF) {
if (ch == '\n') {
printf("  Newline at position %ld\n", pos);
}
pos = ftell(file);
}
fclose(file);
// ============================================================
// TEXT MODE - Positions may be translated
// ============================================================
printf("\nText mode (position translation on some systems):\n");
file = fopen("text_vs_binary.txt", "r");
fseek(file, 0, SEEK_END);
long textSize = ftell(file);
printf("  Text mode size: %ld bytes (may differ on Windows)\n", textSize);
// Seek to positions in text mode (may be less predictable)
fseek(file, 10, SEEK_SET);
printf("  After seek to 10: position %ld\n", ftell(file));
fclose(file);
printf("\nNote on Windows:\n");
printf("  - Text mode: \\r\\n translated to \\n\n");
printf("  - Binary mode: \\r\\n preserved as two bytes\n");
printf("  - fseek() with SEEK_END in text mode is not portable\n");
return 0;
}

Error Handling in File Seeking

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
// ============================================================
// ERROR HANDLING IN FILE SEEKING
// ============================================================
int safeFseek(FILE *file, long offset, int whence) {
// Clear errno before call
errno = 0;
int result = fseek(file, offset, whence);
if (result != 0) {
fprintf(stderr, "fseek error: %s\n", strerror(errno));
// Check specific errors
if (errno == EBADF) {
fprintf(stderr, "  Invalid file stream\n");
} else if (errno == EINVAL) {
fprintf(stderr, "  Invalid whence argument\n");
} else if (errno == ESPIPE) {
fprintf(stderr, "  Seek not supported on this stream\n");
} else if (errno == EOVERFLOW) {
fprintf(stderr, "  Offset too large\n");
}
}
return result;
}
long safeFtell(FILE *file) {
errno = 0;
long pos = ftell(file);
if (pos == -1L) {
fprintf(stderr, "ftell error: %s\n", strerror(errno));
}
return pos;
}
void demonstrateErrors() {
printf("=== Error Handling ===\n\n");
// Test 1: Seek on closed file
FILE *file = fopen("test.txt", "w");
fclose(file);
printf("Seek on closed file:\n");
safeFseek(file, 10, SEEK_SET);
// Test 2: Invalid whence
file = fopen("test.txt", "w");
printf("\nInvalid whence argument:\n");
safeFseek(file, 10, 999);  // Invalid whence
// Test 3: Seek beyond valid range
printf("\nSeek beyond valid range:\n");
rewind(file);
safeFseek(file, -10, SEEK_SET);  // Negative position
// Test 4: Successful seek
printf("\nSuccessful seek:\n");
if (safeFseek(file, 0, SEEK_END) == 0) {
long size = safeFtell(file);
printf("  File size: %ld\n", size);
}
fclose(file);
}
int main() {
demonstrateErrors();
return 0;
}

Practical Applications

#include <stdio.h>
#include <stdlib.h>
// ============================================================
// PRACTICAL APPLICATIONS
// ============================================================
// 1. Tail command implementation (last n lines)
void tail(const char *filename, int n) {
FILE *file = fopen(filename, "rb");
if (file == NULL) {
perror("Error opening file");
return;
}
// Go to end
fseek(file, 0, SEEK_END);
long fileSize = ftell(file);
int linesFound = 0;
long pos = fileSize - 1;
char buffer[1024];
// Scan backwards to find nth newline from end
while (pos >= 0 && linesFound < n) {
fseek(file, pos, SEEK_SET);
int ch = fgetc(file);
if (ch == '\n') {
linesFound++;
}
pos--;
}
// Move to position after the found newline (or start of file)
pos += 2;
fseek(file, pos, SEEK_SET);
// Read and print remaining lines
printf("Last %d lines:\n", n);
while (fgets(buffer, sizeof(buffer), file)) {
printf("%s", buffer);
}
fclose(file);
}
// 2. Fast file size without reading
long getFileSize(const char *filename) {
FILE *file = fopen(filename, "rb");
if (file == NULL) return -1;
fseek(file, 0, SEEK_END);
long size = ftell(file);
fclose(file);
return size;
}
// 3. Check if file is empty
int isFileEmpty(const char *filename) {
FILE *file = fopen(filename, "rb");
if (file == NULL) return 1;  // Treat as empty
fseek(file, 0, SEEK_END);
int empty = (ftell(file) == 0);
fclose(file);
return empty;
}
// 4. Split file into chunks
void splitFile(const char *filename, size_t chunkSize) {
FILE *src = fopen(filename, "rb");
if (src == NULL) return;
long fileSize = getFileSize(filename);
int numChunks = (fileSize + chunkSize - 1) / chunkSize;
char *buffer = malloc(chunkSize);
char outName[256];
for (int i = 0; i < numChunks; i++) {
sprintf(outName, "%s.part%d", filename, i);
FILE *dst = fopen(outName, "wb");
size_t bytesToRead = (i == numChunks - 1) ? 
fileSize - i * chunkSize : chunkSize;
fread(buffer, 1, bytesToRead, src);
fwrite(buffer, 1, bytesToRead, dst);
fclose(dst);
printf("Created %s (%zu bytes)\n", outName, bytesToRead);
}
free(buffer);
fclose(src);
}
// 5. Resume download simulation
typedef struct {
long downloadedBytes;
long totalBytes;
} DownloadState;
void saveDownloadState(DownloadState *state, const char *filename) {
FILE *file = fopen(filename, "wb");
if (file) {
fwrite(state, sizeof(DownloadState), 1, file);
fclose(file);
}
}
DownloadState loadDownloadState(const char *filename) {
DownloadState state = {0, 0};
FILE *file = fopen(filename, "rb");
if (file) {
fread(&state, sizeof(DownloadState), 1, file);
fclose(file);
}
return state;
}
void simulateDownload() {
const char *stateFile = "download.dat";
DownloadState state = loadDownloadState(stateFile);
printf("Resuming download at byte %ld of %ld\n", 
state.downloadedBytes, state.totalBytes);
// Simulate downloading...
while (state.downloadedBytes < state.totalBytes) {
// Download chunk
state.downloadedBytes += 1024;
if (state.downloadedBytes > state.totalBytes) {
state.downloadedBytes = state.totalBytes;
}
// Save progress every 10%
if (state.downloadedBytes % (state.totalBytes / 10) < 1024) {
saveDownloadState(&state, stateFile);
printf("Saved progress: %.1f%%\n", 
(float)state.downloadedBytes / state.totalBytes * 100);
}
}
printf("Download complete!\n");
remove(stateFile);
}
int main() {
printf("=== Practical Applications ===\n\n");
// Create a test file
FILE *file = fopen("test.txt", "w");
for (int i = 1; i <= 50; i++) {
fprintf(file, "Line %d: This is a test line\n", i);
}
fclose(file);
// Test tail function
tail("test.txt", 10);
// File size
long size = getFileSize("test.txt");
printf("\nFile size: %ld bytes\n", size);
// Check if empty
printf("File empty: %s\n", isFileEmpty("test.txt") ? "yes" : "no");
// Split file
printf("\nSplitting file into chunks:\n");
splitFile("test.txt", 500);
return 0;
}

Performance Considerations

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
// ============================================================
// PERFORMANCE CONSIDERATIONS
// ============================================================
void performanceComparison() {
const int SIZE = 100 * 1024 * 1024;  // 100 MB
const int READS = 10000;
// Create large file
FILE *file = fopen("large.dat", "wb");
char *buffer = malloc(SIZE);
for (int i = 0; i < SIZE; i++) {
buffer[i] = rand() % 256;
}
fwrite(buffer, 1, SIZE, file);
fclose(file);
free(buffer);
printf("=== Performance Considerations ===\n\n");
printf("File size: %d MB\n", SIZE / (1024 * 1024));
printf("Number of reads: %d\n\n", READS);
clock_t start, end;
// ============================================================
// Sequential access (fast)
// ============================================================
file = fopen("large.dat", "rb");
start = clock();
for (int i = 0; i < READS; i++) {
// Simulate processing some data
fseek(file, i * 1000, SEEK_SET);
fgetc(file);
}
end = clock();
printf("Sequential-like access: %.3f seconds\n",
(double)(end - start) / CLOCKS_PER_SEC);
fclose(file);
// ============================================================
// Random access (slow)
// ============================================================
file = fopen("large.dat", "rb");
start = clock();
for (int i = 0; i < READS; i++) {
// Random positions
long pos = rand() % SIZE;
fseek(file, pos, SEEK_SET);
fgetc(file);
}
end = clock();
printf("Random access:         %.3f seconds\n",
(double)(end - start) / CLOCKS_PER_SEC);
fclose(file);
// ============================================================
// Impact of seek distance
// ============================================================
printf("\nSeek distance impact:\n");
file = fopen("large.dat", "rb");
// Small seeks
start = clock();
for (int i = 0; i < READS; i++) {
fseek(file, 100, SEEK_CUR);
fgetc(file);
}
end = clock();
printf("  Small seeks (100 bytes): %.3f seconds\n",
(double)(end - start) / CLOCKS_PER_SEC);
// Large seeks
rewind(file);
start = clock();
for (int i = 0; i < READS; i++) {
fseek(file, 100000, SEEK_CUR);
fgetc(file);
}
end = clock();
printf("  Large seeks (100KB):    %.3f seconds\n",
(double)(end - start) / CLOCKS_PER_SEC);
fclose(file);
printf("\nOptimization tips:\n");
printf("  - Minimize seek operations\n");
printf("  - Read larger chunks when possible\n");
printf("  - Sequential access is fastest\n");
printf("  - Consider memory-mapped I/O for random access\n");
printf("  - Buffer frequently accessed data\n");
}
int main() {
performanceComparison();
return 0;
}

Summary Table

FunctionPurposeReturn ValueNotes
fseek(FILE *stream, long offset, int whence)Set file position0 on success, non-zero on errorMost common, uses long offset
ftell(FILE *stream)Get current positionCurrent position or -1L on errorLimited to long range
rewind(FILE *stream)Go to beginningvoidAlso clears error indicators
fgetpos(FILE *stream, fpos_t *pos)Get position0 on success, non-zero on errorMore portable for large files
fsetpos(FILE *stream, fpos_t *pos)Set position0 on success, non-zero on errorUse with positions from fgetpos
fseeko(FILE *stream, off_t offset, int whence)Large file seek0 on success, non-zero on errorUses off_t (64-bit on many systems)
ftello(FILE *stream)Large file tellCurrent position or -1 on errorUses off_t
Seek OriginMeaningExample
SEEK_SETBeginning of filefseek(file, 100, SEEK_SET)
SEEK_CURCurrent positionfseek(file, 20, SEEK_CUR)
SEEK_ENDEnd of filefseek(file, -10, SEEK_END)

Conclusion

File seeking in C provides powerful random access capabilities essential for many applications. Key takeaways:

  1. Three seek origins provide flexibility in positioning:
  • SEEK_SET: Absolute from beginning
  • SEEK_CUR: Relative to current position
  • SEEK_END: Relative to end of file
  1. Common applications include:
  • Database record access
  • File indexing and searching
  • Resumeable downloads
  • File splitting and merging
  • Reading files in reverse
  1. Portability considerations:
  • Text mode vs binary mode affects seek behavior
  • fgetpos/fsetpos are more portable for large files
  • Consider fseeko/ftello for files >2GB
  1. Performance optimization:
  • Sequential access is fastest
  • Minimize seek operations
  • Read larger chunks when possible
  • Buffer frequently accessed data
  1. Error handling is crucial:
  • Always check return values
  • Use errno for detailed error information
  • Handle invalid seeks gracefully

Mastering file seeking transforms file I/O from simple sequential processing to sophisticated random access, enabling efficient data structures and algorithms that work with persistent storage.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper