A Beginner’s Guide to Random File Access in C: Save, Read, and Modify Files Like a Pro

Discover the fundamentals of saving random files in C programming with this beginner-friendly guide. Learn how to open, read, and write to files randomly using practical examples, including file manipulation techniques and the use of fseek(). Ideal for those looking to enhance their C programming skills.
code
c
Author

Steven P. Sanderson II, MPH

Published

April 9, 2025

Keywords

Programming, Saving Random Files, Random File Access, C Programming, File Handling in C, fseek Function, C File I/O, Reading and Writing Files, File Pointer, Sequential File Access, File Modes in C, How to Save Random Files in C Programming, Using fseek() for Random File Access in C, Examples of File Handling in C, Opening and Modifying Random Files in C, Beginner Guide to C File Manipulation Techniques

Author’s Note: Hello fellow learners! I’m writing this series as I continue my own journey with C programming. I believe the best way to learn is to teach, so I’m documenting these concepts as I understand them. We’re all learning together, and I hope my explanations help make these topics clearer for you too!

Introduction: Understanding Random File Access in C

For beginners trying to get into C programming, file handling is an important skill that opens up possibilities for data persistence. While sequential file access (reading or writing files from start to finish) might be familiar to you, today we’ll explore a more flexible approach: random file access.

Random file access allows you to jump to any position within a file, read or modify data at that specific location, and continue working elsewhere in the file. Think of it like a music playlist where you can skip to any song rather than having to listen from beginning to end.

By the end of this article, you’ll understand how to save, read, and manipulate data at any position in a file using C’s random access file functions.

Why Use Random File Access?

Before getting into the how-to, let’s understand why random access is valuable:

  • Efficiency: You can update specific records without rewriting the entire file
  • Flexibility: Read and write operations can be performed in any order
  • Data Management: Ideal for applications that need to update specific portions of data (like user records, game saves, or configuration files)

As one beginner put it: “Sequential files are like cassette tapes, while random access files are like CDs where you can jump to any track instantly.”

Getting Started: Opening Files for Random Access

To work with random access files in C, you first need to open them with the right access mode. Unlike sequential files where you choose either reading or writing mode, random access requires special mode flags.

Random Access File Modes

Here are the three primary modes for random access files:

Mode Description Use Case
"r+" Opens an existing file for both reading and writing When you need to modify an existing file
"w+" Creates a new file (or truncates existing) for both reading and writing When you need a fresh file
"a+" Opens in append mode but allows reading and modification throughout When you want to add data but also modify existing content

Let’s look at how to open a file for random access:

#include <stdio.h>

int main() {
    FILE *filePtr;
    
    // Opening a file for both reading and writing
    filePtr = fopen("data.txt", "r+");
    
    // Always check if file opened successfully
    if (filePtr == NULL) {
        printf("Error opening file!\n");
        return 1;
    }
    
    // File operations would go here
    
    // Close file when done
    fclose(filePtr);
    
    return 0;
}

The key part to notice is "r+" which tells C that we want to both read from and write to this file.

Your First Random Access Program: Writing and Reading the Alphabet

Let’s implement a simple program that writes the letters A through Z to a file and then reads them backward:

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

int main() {
    FILE *filePtr;
    char letter;
    int i;
    
    // Open file for both writing and reading
    filePtr = fopen("letters.txt", "w+");
    
    // Always check if file opened successfully
    if (filePtr == NULL) {
        printf("Error opening file!\n");
        return 1;
    }
    
    // Write A to Z to the file
    for (letter = 'A'; letter <= 'Z'; letter++) {
        fputc(letter, filePtr);
    }
    
    printf("Just wrote the letters A through Z\n");
    
    // Position file pointer at the last character (Z)
    // -1 because pointer is positioned AFTER the last write
    fseek(filePtr, -1, SEEK_END);
    
    printf("Here is the file backwards:\n");
    
    // Read backwards from Z to A
    for (i = 26; i > 0; i--) {
        letter = fgetc(filePtr);  // Read current character
        printf("The next letter is %c.\n", letter);
        
        // Move back 2 positions (1 for the character we just read,
        // and 1 more to go to the previous character)
        if (i > 1) {  // Avoid seeking before beginning of file on last iteration
            fseek(filePtr, -2, SEEK_CUR);
        }
    }
    
    // Always close the file
    fclose(filePtr);
    
    return 0;
}

This example demonstrates several key concepts:

  1. Opening a file with "w+" mode (create new + read/write)
  2. Writing characters with fputc()
  3. Using fseek() to position the file pointer at the end
  4. Reading characters with fgetc()
  5. Moving backward through the file with negative offsets

Modifying Data: Changing Specific File Positions

Now let’s create a program that demonstrates how to modify specific positions in an existing file. This program will ask the user which letter they want to replace with an asterisk:

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

int main() {
    FILE *filePtr;
    char letter;
    int position;
    
    // Open existing file for reading and writing
    filePtr = fopen("letters.txt", "r+");
    
    if (filePtr == NULL) {
        printf("Error opening file. Make sure letters.txt exists.\n");
        return 1;
    }
    
    // Ask user which letter to change
    printf("Which letter position would you like to change (1-26)? ");
    scanf("%d", &position);
    
    // Validate input
    if (position < 1 || position > 26) {
        printf("Invalid position! Must be between 1 and 26.\n");
        fclose(filePtr);
        return 1;
    }
    
    // Move to the specified position (subtract 1 because file positions start at 0)
    fseek(filePtr, position-1, SEEK_SET);
    
    // Replace the letter with an asterisk
    fputc('*', filePtr);
    
    // Return to beginning of file to read all letters
    fseek(filePtr, 0, SEEK_SET);
    
    // Read and display the modified content
    printf("\nHere is the modified alphabet:\n");
    for (int i = 0; i < 26; i++) {
        letter = fgetc(filePtr);
        printf("%c ", letter);
    }
    printf("\n");
    
    // Close the file
    fclose(filePtr);
    
    return 0;
}

In this example:

  1. We open the file in "r+" mode (existing file + read/write)
  2. We use fseek() with SEEK_SET to position the pointer at the exact letter we want to change
  3. We write an asterisk to that position using fputc()
  4. We then return to the beginning of the file and read all letters to confirm the change

Your Turn!

Now that you’ve seen how random file access works, try this exercise:

Create a program that:

  1. Writes numbers 1-10 to a file
  2. Asks the user for a position and a new number
  3. Changes the number at that position
  4. Displays the modified list
Click to See Solution!
#include <stdio.h>
#include <stdlib.h>

int main() {
    FILE *filePtr;
    int i, position, newNumber;
    
    // Create file with numbers 1-10
    filePtr = fopen("numbers.txt", "w+");
    
    if (filePtr == NULL) {
        printf("Error creating file.\n");
        return 1;
    }
    
    // Write numbers 1-10 to file
    for (i = 1; i <= 10; i++) {
        fprintf(filePtr, "%d\n", i);
    }
    
    // Get position and new number from user
    printf("Which position do you want to change (1-10)? ");
    scanf("%d", &position);
    
    if (position < 1 || position > 10) {
        printf("Invalid position!\n");
        fclose(filePtr);
        return 1;
    }
    
    printf("Enter the new number: ");
    scanf("%d", &newNumber);
    
    // Position file pointer (each number takes 2 bytes: digit + newline)
    fseek(filePtr, (position-1)*2, SEEK_SET);
    
    // Write new number
    fprintf(filePtr, "%d\n", newNumber);
    
    // Return to beginning and display all numbers
    fseek(filePtr, 0, SEEK_SET);
    
    printf("\nUpdated numbers:\n");
    for (i = 1; i <= 10; i++) {
        char buffer[10];
        fgets(buffer, sizeof(buffer), filePtr);
        printf("%d: %s", i, buffer);
    }
    
    // Close file
    fclose(filePtr);
    
    return 0;
}

Advanced Example: A Simple Database

Let’s put everything together with a more practical example: a simple database of names that can be created, read, updated, and displayed:

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

#define NAME_LENGTH 30
#define MAX_RECORDS 10

struct Person {
    char name[NAME_LENGTH];
    int age;
};

void displayMenu();
void addRecord(FILE *file);
void viewRecords(FILE *file);
void updateRecord(FILE *file);

int main() {
    FILE *dataFile;
    int choice;
    
    // Open database file
    dataFile = fopen("database.dat", "w+");
    
    if (dataFile == NULL) {
        printf("Error creating database file!\n");
        return 1;
    }
    
    // Initialize database with empty records
    struct Person emptyPerson = {"", 0};
    for (int i = 0; i < MAX_RECORDS; i++) {
        fwrite(&emptyPerson, sizeof(struct Person), 1, dataFile);
    }
    
    // Program loop
    do {
        displayMenu();
        scanf("%d", &choice);
        
        switch (choice) {
            case 1: addRecord(dataFile); break;
            case 2: viewRecords(dataFile); break;
            case 3: updateRecord(dataFile); break;
            case 4: printf("Exiting program.\n"); break;
            default: printf("Invalid option. Try again.\n");
        }
    } while (choice != 4);
    
    // Close file
    fclose(dataFile);
    
    return 0;
}

void displayMenu() {
    printf("\n==== Simple Database ====\n");
    printf("1. Add a record\n");
    printf("2. View all records\n");
    printf("3. Update a record\n");
    printf("4. Exit\n");
    printf("Enter your choice: ");
}

void addRecord(FILE *file) {
    struct Person newPerson;
    int recordNum;
    
    printf("Which record number to add (1-%d)? ", MAX_RECORDS);
    scanf("%d", &recordNum);
    
    if (recordNum < 1 || recordNum > MAX_RECORDS) {
        printf("Invalid record number.\n");
        return;
    }
    
    // Clear input buffer
    getchar();
    
    printf("Enter name: ");
    fgets(newPerson.name, NAME_LENGTH, stdin);
    newPerson.name[strcspn(newPerson.name, "\n")] = 0; // Remove newline
    
    printf("Enter age: ");
    scanf("%d", &newPerson.age);
    
    // Position file pointer to the correct record
    fseek(file, (recordNum-1) * sizeof(struct Person), SEEK_SET);
    
    // Write the record
    fwrite(&newPerson, sizeof(struct Person), 1, file);
    
    printf("Record added successfully!\n");
}

void viewRecords(FILE *file) {
    struct Person person;
    
    printf("\n=== All Records ===\n");
    
    // Go to beginning of file
    fseek(file, 0, SEEK_SET);
    
    // Read and display each record
    for (int i = 0; i < MAX_RECORDS; i++) {
        fread(&person, sizeof(struct Person), 1, file);
        
        printf("Record #%d:\n", i+1);
        printf("  Name: %s\n", person.name[0] ? person.name : "(empty)");
        printf("  Age: %d\n", person.age);
        printf("-------------------\n");
    }
}

void updateRecord(FILE *file) {
    struct Person person;
    int recordNum;
    
    printf("Which record number to update (1-%d)? ", MAX_RECORDS);
    scanf("%d", &recordNum);
    
    if (recordNum < 1 || recordNum > MAX_RECORDS) {
        printf("Invalid record number.\n");
        return;
    }
    
    // Position file pointer to the correct record
    fseek(file, (recordNum-1) * sizeof(struct Person), SEEK_SET);
    
    // Read the existing record
    fread(&person, sizeof(struct Person), 1, file);
    
    printf("Current Name: %s\n", person.name);
    printf("Current Age: %d\n", person.age);
    
    // Clear input buffer
    getchar();
    
    printf("Enter new name (or press Enter to keep current): ");
    char tempName[NAME_LENGTH];
    fgets(tempName, NAME_LENGTH, stdin);
    
    // Only update if something was entered
    if (tempName[0] != '\n') {
        tempName[strcspn(tempName, "\n")] = 0; // Remove newline
        strcpy(person.name, tempName);
    }
    
    printf("Enter new age (or -1 to keep current): ");
    int tempAge;
    scanf("%d", &tempAge);
    
    if (tempAge != -1) {
        person.age = tempAge;
    }
    
    // Move back to the record position
    fseek(file, (recordNum-1) * sizeof(struct Person), SEEK_SET);
    
    // Write the updated record
    fwrite(&person, sizeof(struct Person), 1, file);
    
    printf("Record updated successfully!\n");
}

This advanced example demonstrates:

  1. Structured data storage with fwrite() and fread()
  2. Using fseek() to access specific records
  3. Reading, writing, and updating data at any position

Key Takeaways: Mastering Random File Access in C

  • File Access Modes: Use "r+", "w+", or "a+" to open files for random access
  • Navigation: fseek() is your primary tool for moving around in a file
  • Position Origin: Remember the three origin points: SEEK_SET (beginning), SEEK_CUR (current position), and SEEK_END (end of file)
  • Safety Checks: Always verify that files opened successfully before using them
  • File Closure: Don’t forget to close files with fclose() when done
  • Data Positioning: Be careful with offsets in fseek() to ensure you’re at the correct position
  • Two-Way Access: Random files allow both reading and writing without reopening

Common Challenges and Troubleshooting

When working with random files, beginners often encounter these issues:

  1. Incorrect File Position: Keep track of your position, especially after reading or writing data
  2. Buffer Issues: Make sure to flush buffers with fflush() if switching between reading and writing
  3. Overwriting Data: Be careful when replacing data to ensure new data fits in the allocated space
  4. File Not Found: Always check if fopen() returned NULL before proceeding

Frequently Asked Questions

1. What’s the difference between sequential and random file access?

Sequential access reads/writes files in order from beginning to end, while random access lets you jump to any position in the file to read or write.

2. Can I convert a sequential file to random access?

Yes, any file can be opened in random access mode. The file format doesn’t determine access method—the way you open and use it does.

3. Do I need to close and reopen a file to switch between reading and writing?

No, with random access modes ("r+", "w+", "a+"), you can both read and write without reopening the file.

4. Is there a way to find the current position in a file?

Yes, use the ftell() function, which returns the current position as a long integer.

5. How do I determine the size of a file?

You can use fseek(file, 0, SEEK_END) to move to the end of the file, then use ftell() to get the position, which equals the file size in bytes.

Conclusion: Putting Your Random Access Skills to Work

Random file access is a powerful tool in your C programming toolkit. It enables you to create more sophisticated applications that can efficiently manage data by updating specific parts without rewriting entire files.

By mastering functions like fopen(), fseek(), fread(), and fwrite(), you’ve gained the ability to create, read, and modify data at any position in a file—opening the door to databases, game save systems, configuration managers, and many other practical applications.

Remember that the key to successful file manipulation is careful position tracking and proper error checking. With these skills and a bit of practice, you’ll be creating robust file-based applications in no time.

What will you build with your new random file access skills? I’d love to hear about your projects in the comments below!


Did you find this guide helpful? Share it with other beginner C programmers who might benefit from understanding random file access. And stay tuned for more articles in this C programming series!


References and Further Reading

  1. The GNU C Library: File Positioning - Official documentation on file positioning functions including fseek() and related functions.

  2. Microsoft C Runtime Library Documentation - Detailed explanation of fseek() implementation in Microsoft’s C runtime.

  3. C File I/O and Binary File Operations - A comprehensive guide to file operations in C including random access techniques.

  4. GeeksforGeeks: C File Handling - Collection of tutorials on file handling in C with practical examples.

  5. Stanford CS Education Library: File Access in C - Academic resource with explanations of file access patterns.


Happy Coding! 🚀

Save Random Files in C

You can connect with me at any one of the below:

Telegram Channel here: https://t.me/steveondata

LinkedIn Network here: https://www.linkedin.com/in/spsanderson/

Mastadon Social here: https://mstdn.social/@stevensanderson

RStats Network here: https://rstats.me/@spsanderson

GitHub Network here: https://github.com/spsanderson

Bluesky Network here: https://bsky.app/profile/spsanderson.com

My Book: Extending Excel with Python and R here: https://packt.link/oTyZJ

You.com Referral Link: https://you.com/join/EHSLDTL6