C feof function
last modified April 6, 2025
File handling in C requires precise detection of end-of-file conditions to avoid
errors and ensure proper data processing. The feof
function is a
critical tool for this purpose, indicating when a file stream has reached its
end. This tutorial explores feof
in depth, explaining its proper
usage and demonstrating practical applications through clear examples.
Understanding feof
helps prevent common file reading mistakes and
ensures robust file operations.
What Is feof?
The feof
function checks if the end-of-file indicator is set for a
given file stream. It takes a FILE
pointer as its argument and
returns a non-zero value if EOF is reached. Unlike checking for the
EOF
constant directly, feof
examines the stream's
internal state. It's important to use feof
after a read operation,
not before, to get accurate results. Proper usage prevents infinite loops and
ensures complete file processing.
Basic feof Example
This simple example demonstrates the fundamental usage of feof
to
read a file until the end is reached.
#include <stdio.h> int main() { FILE *fp = fopen("data.txt", "r"); if (fp == NULL) { perror("Error opening file"); return 1; } int ch; while (1) { ch = fgetc(fp); if (feof(fp)) { break; // Exit loop when EOF is reached } putchar(ch); } fclose(fp); return 0; }
This program opens "data.txt" and reads it character by character. The
feof
function checks if we've reached the end of file after each
read. When feof
returns true, the loop breaks. Note that we check
for EOF after reading, not before, to ensure we process all data correctly.
Always close the file with fclose
when done.
Reading Lines Until EOF
This example shows how to read an entire file line by line using
feof
with fgets
.
#include <stdio.h> #include <string.h> #define MAX_LEN 256 int main() { FILE *fp = fopen("lines.txt", "r"); if (fp == NULL) { perror("File open error"); return 1; } char buffer[MAX_LEN]; while (!feof(fp)) { if (fgets(buffer, MAX_LEN, fp) != NULL) { // Remove newline if present buffer[strcspn(buffer, "\n")] = '\0'; printf("Read: %s\n", buffer); } } fclose(fp); return 0; }
Here we use feof
as the loop condition to continue reading until EOF
is reached. The fgets
function reads each line into a buffer, which
we then process. Note the check for NULL
from fgets
-
this is important because feof
only returns true after a read
attempt fails. The combination ensures we process all lines correctly.
Binary File Processing with feof
This example demonstrates using feof
with binary file operations.
#include <stdio.h> struct Record { int id; double value; }; int main() { FILE *fp = fopen("data.bin", "rb"); if (fp == NULL) { perror("Binary file open error"); return 1; } struct Record rec; while (!feof(fp)) { size_t read = fread(&rec, sizeof(struct Record), 1, fp); if (read == 1) { printf("ID: %d, Value: %.2f\n", rec.id, rec.value); } } fclose(fp); return 0; }
When working with binary files, feof
helps detect the end of file
while reading structured data. We read Record
structures until
feof
indicates we've reached the end. The fread
return
value is checked to ensure we only process complete records. This approach works
well for fixed-size binary data structures.
Common feof Pitfall
This example illustrates a common mistake when using feof
and how to
avoid it.
#include <stdio.h> // WRONG way to use feof void wrong_approach(FILE *fp) { char ch; while (!feof(fp)) { // Check before read - BAD! ch = fgetc(fp); putchar(ch); // May process EOF as data } } // RIGHT way to use feof void correct_approach(FILE *fp) { int ch; while ((ch = fgetc(fp)) != EOF) { // Check read result putchar(ch); } } int main() { FILE *fp = fopen("sample.txt", "r"); if (fp == NULL) { perror("File error"); return 1; } printf("Incorrect output:\n"); wrong_approach(fp); rewind(fp); // Reset to file start printf("\nCorrect output:\n"); correct_approach(fp); fclose(fp); return 0; }
The wrong approach checks feof
before reading, which can lead to
processing an extra "phantom" character. The correct way checks the read
operation's result directly against EOF
. This example clearly shows
why you should never use feof
as the sole loop condition without
checking the read operation's success. The correct approach is more reliable and
avoids common errors.
Counting File Size with feof
This example uses feof
to count the number of bytes in a file.
#include <stdio.h> int main() { FILE *fp = fopen("largefile.bin", "rb"); if (fp == NULL) { perror("File open failed"); return 1; } long count = 0; while (!feof(fp)) { if (fgetc(fp) != EOF) { // Read and check count++; } } printf("File contains %ld bytes\n", count); fclose(fp); return 0; }
This program counts each byte in a binary file until reaching EOF. We use
feof
in the loop condition but still check each
fgetc
result to ensure we only count successful reads. For very
large files, consider using fseek
and ftell
instead,
but this method works well for demonstrating feof
with byte-level
processing. Always close files when done counting.
Processing CSV Data with feof
This example shows how to process CSV data while properly handling EOF conditions.
#include <stdio.h> #include <string.h> #define MAX_LINE 1024 int main() { FILE *fp = fopen("data.csv", "r"); if (fp == NULL) { perror("CSV open error"); return 1; } char line[MAX_LINE]; int row_count = 0; while (!feof(fp)) { if (fgets(line, MAX_LINE, fp) != NULL) { // Process CSV line char *token = strtok(line, ","); printf("Row %d: ", ++row_count); while (token != NULL) { printf("[%s] ", token); token = strtok(NULL, ","); } printf("\n"); } } printf("Processed %d rows\n", row_count); fclose(fp); return 0; }
This CSV processor uses feof
to control the outer loop while
checking fgets
for successful line reads. Each line is split into
comma-separated tokens for processing. The combination of feof
and
NULL
checking ensures we process all data without missing lines or
processing empty ones. This pattern works well for many text file formats beyond
just CSV.
Custom File Reader Function
This example demonstrates a robust file reading function that properly handles EOF conditions.
#include <stdio.h> #include <stdbool.h> bool read_next_chunk(FILE *fp, char *buffer, size_t size) { if (feof(fp)) { return false; // Already at EOF } size_t read = fread(buffer, 1, size, fp); if (read == 0) { return false; // Read failed or EOF } buffer[read] = '\0'; // Null-terminate return true; } int main() { FILE *fp = fopen("document.txt", "r"); if (fp == NULL) { perror("File open failed"); return 1; } char chunk[256]; while (read_next_chunk(fp, chunk, sizeof(chunk) - 1)) { printf("Chunk: %s\n", chunk); } if (feof(fp)) { printf("Reached end of file\n"); } else { printf("Error before EOF\n"); } fclose(fp); return 0; }
This example shows a more advanced use of feof
in a custom file
reading function. The function checks for EOF before attempting to read, then
verifies the read operation succeeded. The main loop uses the function's return
value to control processing. After the loop, we check feof
again to
determine if we reached the end normally or encountered an error. This pattern
creates robust, reusable file processing code.
Best Practices for Using feof
- Check After Reads: Always use
feof
after read operations, not before. - Combine with Read Checks: Verify read operation success along with
feof
. - Avoid as Sole Condition: Don't use
feof
alone as a loop condition. - Handle Binary Files Carefully: Be extra cautious with binary data and EOF conditions.
- Clear Errors When Needed: Use
clearerr
to reset EOF/error flags if reusing streams.
Source
This tutorial has explored the feof
function in depth, showing its
proper usage through practical examples. Mastering feof
is
essential for robust file handling in C, preventing common errors and ensuring
complete data processing. Remember to always combine feof
checks
with read operation verification for the most reliable results.
Author
List C Standard Library.