Understanding File I/O: Reading and Writing Files in Python and C

File Input/Output (I/O) is an essential skill for any programmer. Whether you’re saving user data, reading configuration files, or logging events, understanding how to interact with files is crucial. In this lesson, we’ll explore how to work with files in Python, a high-level language, and C, which provides a lower-level approach using file descriptors.


What is File I/O?

File I/O refers to the process of reading from and writing to files stored on a computer. Files can be:

  • Text files (e.g., .txt, .csv) containing human-readable text.
  • Binary files (e.g., .exe, .png) containing raw data.

File operations generally follow these steps:

  1. Open the file (for reading or writing).
  2. Perform operations (read, write, append, etc.).
  3. Close the file (to free resources).

Now, let’s see how this works in Python and C.


File I/O in Python

Python provides a simple and intuitive way to handle file operations using the built-in open() function.

Opening a File

# Open a file for reading ('r' mode)
file = open("example.txt", "r")

Common File Modes:

  • 'r' – Read (default mode, file must exist)
  • 'w' – Write (creates a new file or overwrites an existing one)
  • 'a' – Append (adds data to an existing file)
  • 'rb', 'wb', 'ab' – Binary versions of read, write, and append

Reading from a File

file = open("example.txt", "r")
content = file.read()
print(content)
file.close()

Alternatively, use with to handle files more safely:

with open("example.txt", "r") as file:
    content = file.read()
    print(content)  # File automatically closes after this block

Writing to a File

with open("example.txt", "w") as file:
    file.write("Hello, world!\n")

Appending to a File

with open("example.txt", "a") as file:
    file.write("Adding another line!\n")

Python handles file operations at a high level, abstracting the complexities behind functions like open(). However, at a lower level, many languages, including Python, rely on file descriptors.


File I/O in C (Introducing File Handles)

C provides a higher level API for accessing files, known as file handles. A file handle references an file which has been opened through the fopen call. When you are done with it, you need to call fclose to avoid leaking the file handle.

Opening a File

#include <stdio.h>

int main() {
    FILE *file = fopen("example.txt", "r");
    if (file == NULL) {
        printf("Error opening file!\n");
        return 1;
    }
    fclose(file);
    return 0;
}

Reading from a File

#include <stdio.h>

int main() {
    FILE *file = fopen("example.txt", "r");
    char buffer[100];

    if (file == NULL) {
        printf("Error opening file!\n");
        return 1;
    }

    while (fgets(buffer, 100, file) != NULL) {
        printf("%s", buffer);
    }

    fclose(file);
    return 0;
}

Writing to a File

#include <stdio.h>

int main() {
    FILE *file = fopen("example.txt", "w");
    if (file == NULL) {
        printf("Error opening file!\n");
        return 1;
    }

    fputs("Hello, world!\n", file);
    fclose(file);
    return 0;
}

File Descriptors in C

For even lower-level file operations, C provides system calls like open() and read(), which return and use file descriptors. Similar to the file handles, you need to call close() when you are finished to avoid leaking the file descriptor.

#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>

int main() {
    int fd = open("example.txt", O_RDONLY);
    if (fd == -1) {
        printf("Error opening file!\n");
        return 1;
    }

    char buffer[100];
    read(fd, buffer, 100);
    printf("%s", buffer);

    close(fd);
    return 0;
}

Understanding File Descriptors

  • A file descriptor is an integer returned by open().
  • Standard file descriptors include:
    • 0 – Standard Input (stdin)
    • 1 – Standard Output (stdout)
    • 2 – Standard Error (stderr)

Using file descriptors allows for greater control over system-level file operations, making them a fundamental concept across programming languages.


Summary

Feature Python C (High-Level) C (Low-Level)
Ease of Use Simple open() Uses fopen() Uses open() and descriptors
Safety Auto-closes with with Requires fclose() Requires close(fd)
Control Abstracted More manual control Full control over system calls

Python makes file operations simple, while C introduces file descriptors, a fundamental concept in low-level programming. Understanding both approaches will give you a deeper grasp of how operating systems handle files.


Next Steps

  • Try reading and writing files in both Python and C.
  • Experiment with file descriptors by redirecting output in C.
  • Explore how other languages (e.g., Java, Rust) handle file I/O.

Got questions? Drop a comment below or share your own file-handling projects!

Happy coding!