Docs
README
Reading Files in C
Table of Contents
- ā¢Introduction
- ā¢Reading Characters - fgetc() and getc()
- ā¢Reading Strings - fgets()
- ā¢Formatted Reading - fscanf()
- ā¢Reading Binary Data - fread()
- ā¢Reading Lines with getline()
- ā¢End of File (EOF) Detection
- ā¢Reading Entire File
- ā¢Common Reading Patterns
- ā¢Error Handling While Reading
- ā¢Performance Considerations
- ā¢Best Practices
- ā¢Summary
Introduction
Reading data from files is a fundamental operation in C programming. C provides multiple functions for reading, each suited for different scenarios:
| Function | Purpose | Best For |
|---|---|---|
fgetc() | Read single character | Character-by-character processing |
fgets() | Read line/string | Line-oriented text files |
fscanf() | Formatted reading | Structured data with known format |
fread() | Binary reading | Binary files, bulk data |
getline() | Read line (dynamic) | Lines of unknown length |
Reading Characters - fgetc() and getc()
fgetc() Function
Reads a single character from a file.
int fgetc(FILE *stream);
Returns:
- ā¢Character read as
unsigned charcast toint - ā¢
EOFon end-of-file or error
Example:
#include <stdio.h>
int main() {
FILE *fp = fopen("input.txt", "r");
if (fp == NULL) {
perror("Error");
return 1;
}
int ch; // Use int, not char!
while ((ch = fgetc(fp)) != EOF) {
putchar(ch); // Print the character
}
fclose(fp);
return 0;
}
Why Use int Instead of char?
ASCII characters: 0 to 127 (or 255 for extended)
EOF constant: -1 (typically)
If you use char:
- Some systems treat char as unsigned (0-255)
- EOF (-1) becomes 255, which is a valid character!
- Loop never terminates
Always use int for fgetc() return value.
getc() Function
int getc(FILE *stream);
- ā¢Functionally identical to
fgetc() - ā¢May be implemented as a macro (faster but less safe)
- ā¢
fgetc()is always a function (safer)
/* Both work the same way */
int c1 = fgetc(fp);
int c2 = getc(fp);
Character Reading Workflow
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā Character Reading Process ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā ā
ā āāāāāāāāāāāā āāāāāāāāāāāāāā āāāāāāāāāāāāāāāā ā
ā ā File āāāāāŗā fgetc() āāāāāŗā int ch ā ā
ā ā [A][B][\n]ā ā ā ā (or EOF) ā ā
ā āāāāāāāāāāāā āāāāāāāāāāāāāā āāāāāāāāāāāāāāāā ā
ā ^ ā ā
ā ā ā¼ ā
ā āāāāāā“āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā Position moves forward after each fgetc() ā ā
ā ā First call: returns 'A', position ā B ā ā
ā ā Second call: returns 'B', position ā \n ā ā
ā ā Third call: returns '\n', position ā EOF ā ā
ā ā Fourth call: returns EOF ā ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ungetc() - Push Character Back
int ungetc(int c, FILE *stream);
Pushes a character back onto the input stream.
int ch = fgetc(fp);
if (ch == '#') {
ungetc(ch, fp); // Put it back
// ... handle comment line differently
}
Important: Only one character of pushback is guaranteed.
Reading Strings - fgets()
Syntax
char *fgets(char *str, int n, FILE *stream);
Parameters
| Parameter | Description |
|---|---|
str | Buffer to store the string |
n | Maximum characters to read (including null terminator) |
stream | File pointer |
Returns
- ā¢Pointer to
stron success - ā¢
NULLon EOF or error
How fgets() Works
File content: "Hello World!\nSecond line\n"
Buffer size: 10
Call 1: fgets(buf, 10, fp)
buf = "Hello Wor" + '\0' (9 chars + null)
Call 2: fgets(buf, 10, fp)
buf = "ld!\n" + '\0' (includes newline!)
Call 3: fgets(buf, 10, fp)
buf = "Second li" + '\0'
Call 4: fgets(buf, 10, fp)
buf = "ne\n" + '\0'
Call 5: fgets(buf, 10, fp)
Returns NULL (EOF)
Complete Example
#include <stdio.h>
#include <string.h>
int main() {
FILE *fp = fopen("data.txt", "r");
if (fp == NULL) {
perror("Error opening file");
return 1;
}
char buffer[256];
int line_number = 0;
while (fgets(buffer, sizeof(buffer), fp) != NULL) {
line_number++;
/* Remove trailing newline if present */
size_t len = strlen(buffer);
if (len > 0 && buffer[len-1] == '\n') {
buffer[len-1] = '\0';
}
printf("Line %d: %s\n", line_number, buffer);
}
fclose(fp);
return 0;
}
fgets() vs gets()
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā fgets() vs gets() ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā ā
ā gets() - NEVER USE THIS FUNCTION! ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā - Removed from C11 standard ā
ā - No buffer size limit ā
ā - Classic buffer overflow vulnerability ā
ā - DANGEROUS and DEPRECATED ā
ā ā
ā fgets() - Safe alternative ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā - Requires buffer size parameter ā
ā - Never writes past buffer boundary ā
ā - Keeps the newline character (needs removal) ā
ā - Always use this instead of gets() ā
ā ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Removing the Newline
Several methods to remove trailing newline from fgets() input:
/* Method 1: Using strlen */
char *line = fgets(buffer, sizeof(buffer), fp);
if (line != NULL) {
size_t len = strlen(line);
if (len > 0 && line[len-1] == '\n') {
line[len-1] = '\0';
}
}
/* Method 2: Using strcspn */
buffer[strcspn(buffer, "\n")] = '\0';
/* Method 3: Using strchr */
char *newline = strchr(buffer, '\n');
if (newline) *newline = '\0';
Formatted Reading - fscanf()
Syntax
int fscanf(FILE *stream, const char *format, ...);
Returns
- ā¢Number of successfully matched and assigned items
- ā¢
EOFon end-of-file before any conversion
Common Format Specifiers
| Specifier | Type | Description |
|---|---|---|
%d | int | Decimal integer |
%ld | long | Long integer |
%f | float | Floating point |
%lf | double | Double precision |
%c | char | Single character |
%s | char[] | String (word) |
%[...] | char[] | Scanset (custom character set) |
Basic Usage
#include <stdio.h>
int main() {
FILE *fp = fopen("numbers.txt", "r");
if (fp == NULL) return 1;
int num;
/* Read integers until EOF */
while (fscanf(fp, "%d", &num) == 1) {
printf("Read: %d\n", num);
}
fclose(fp);
return 0;
}
Reading Structured Data
/* File format: name age salary */
/* Example: John 25 50000.00 */
#include <stdio.h>
int main() {
FILE *fp = fopen("employees.txt", "r");
if (fp == NULL) return 1;
char name[50];
int age;
float salary;
while (fscanf(fp, "%49s %d %f", name, &age, &salary) == 3) {
printf("Name: %s, Age: %d, Salary: %.2f\n",
name, age, salary);
}
fclose(fp);
return 0;
}
Reading CSV-like Data
/* File format: name,age,city */
#include <stdio.h>
int main() {
FILE *fp = fopen("data.csv", "r");
if (fp == NULL) return 1;
char name[50], city[50];
int age;
/* Note: %[^,] reads until comma */
while (fscanf(fp, "%49[^,],%d,%49[^\n]\n", name, &age, city) == 3) {
printf("%s is %d years old from %s\n", name, age, city);
}
fclose(fp);
return 0;
}
fscanf() Pitfalls
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā fscanf() Common Issues ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā ā
ā 1. %s stops at whitespace ā
ā Input: "John Smith" ā
ā fscanf(fp, "%s", name) ā name = "John" only! ā
ā ā
ā 2. Leftover newlines ā
ā After reading numbers, '\n' stays in buffer ā
ā Next fgets() may read empty line ā
ā Solution: add space before %c or use fgets() instead ā
ā ā
ā 3. No buffer overflow protection by default ā
ā fscanf(fp, "%s", buf) ā DANGEROUS! ā
ā fscanf(fp, "%49s", buf) ā Safe (49 chars + null) ā
ā ā
ā 4. Mixing fscanf() and fgets() ā
ā fscanf() leaves newline; fgets() reads it ā
ā Consume newline: fscanf(fp, "%d\n", &x) or fgetc(fp) ā
ā ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Always Check Return Value
int count = fscanf(fp, "%d %d %d", &a, &b, &c);
if (count == EOF) {
printf("End of file or error before reading\n");
} else if (count < 3) {
printf("Only read %d values (expected 3)\n", count);
} else {
printf("Successfully read all 3 values\n");
}
Reading Binary Data - fread()
Syntax
size_t fread(void *ptr, size_t size, size_t count, FILE *stream);
Parameters
| Parameter | Description |
|---|---|
ptr | Pointer to buffer for storing data |
size | Size of each element in bytes |
count | Number of elements to read |
stream | File pointer |
Returns
- ā¢Number of elements successfully read
- ā¢May be less than
counton EOF or error
Basic Example
#include <stdio.h>
int main() {
FILE *fp = fopen("data.bin", "rb"); // Note: "rb" for binary
if (fp == NULL) return 1;
int numbers[10];
size_t read = fread(numbers, sizeof(int), 10, fp);
printf("Read %zu integers:\n", read);
for (size_t i = 0; i < read; i++) {
printf(" %d\n", numbers[i]);
}
fclose(fp);
return 0;
}
Reading Structures
#include <stdio.h>
struct Student {
int id;
char name[50];
float gpa;
};
int main() {
FILE *fp = fopen("students.dat", "rb");
if (fp == NULL) return 1;
struct Student student;
while (fread(&student, sizeof(struct Student), 1, fp) == 1) {
printf("ID: %d, Name: %s, GPA: %.2f\n",
student.id, student.name, student.gpa);
}
fclose(fp);
return 0;
}
Bulk Reading for Performance
#include <stdio.h>
#include <stdlib.h>
int main() {
FILE *fp = fopen("large_data.bin", "rb");
if (fp == NULL) return 1;
/* Get file size */
fseek(fp, 0, SEEK_END);
long file_size = ftell(fp);
rewind(fp);
/* Allocate buffer */
char *buffer = malloc(file_size);
if (buffer == NULL) {
fclose(fp);
return 1;
}
/* Read entire file at once */
size_t read = fread(buffer, 1, file_size, fp);
printf("Read %zu bytes\n", read);
free(buffer);
fclose(fp);
return 0;
}
fread() Return Value Details
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā fread() Return Value Analysis ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā ā
ā fread(buffer, 4, 10, fp); // Read 10 items of 4 bytes ā
ā ā
ā Return = 10: All 10 items read successfully ā
ā Return = 5: Only 5 items available (partial read) ā
ā Return = 0: Either EOF or error occurred ā
ā ā
ā To distinguish EOF from error: ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā if (fread(...) < count) { ā ā
ā ā if (feof(fp)) { ā ā
ā ā // End of file reached ā ā
ā ā } else if (ferror(fp)) { ā ā
ā ā // Error occurred ā ā
ā ā } ā ā
ā ā } ā ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Reading Lines with getline()
POSIX getline() Function
ssize_t getline(char **lineptr, size_t *n, FILE *stream);
Note: This is a POSIX extension, not standard C. Available on Linux/Unix.
Parameters
| Parameter | Description |
|---|---|
lineptr | Pointer to buffer pointer (can be NULL) |
n | Pointer to buffer size |
stream | File pointer |
Returns
- ā¢Number of characters read (including newline, excluding null)
- ā¢-1 on failure or EOF
Advantages Over fgets()
- ā¢Automatically allocates/reallocates buffer
- ā¢Handles any line length
- ā¢Returns actual length read
Example
#define _GNU_SOURCE /* Required for getline on some systems */
#include <stdio.h>
#include <stdlib.h>
int main() {
FILE *fp = fopen("text.txt", "r");
if (fp == NULL) return 1;
char *line = NULL;
size_t len = 0;
ssize_t read;
while ((read = getline(&line, &len, fp)) != -1) {
printf("Line length: %zd, Buffer size: %zu\n", read, len);
printf("Content: %s", line);
}
free(line); // Don't forget to free!
fclose(fp);
return 0;
}
Memory Management
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā getline() Memory Behavior ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā ā
ā First call (line = NULL, len = 0): ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā getline allocates buffer internally ā ā
ā ā Updates line pointer and len ā ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā
ā Subsequent calls: ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā If line fits: reuses existing buffer ā ā
ā ā If too small: realloc to larger size ā ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā
ā After loop: ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā MUST free(line) to avoid memory leak ā ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
End of File (EOF) Detection
Two Ways to Detect EOF
1. Check Return Values:
/* For fgetc */
int ch;
while ((ch = fgetc(fp)) != EOF) {
// process ch
}
/* For fgets */
char buffer[100];
while (fgets(buffer, sizeof(buffer), fp) != NULL) {
// process buffer
}
/* For fscanf */
int num;
while (fscanf(fp, "%d", &num) == 1) {
// process num
}
2. Use feof() After Reading:
while (!feof(fp)) { // This is often WRONG!
int ch = fgetc(fp);
// ch might be EOF here!
}
// CORRECT approach:
int ch;
while ((ch = fgetc(fp)) != EOF) {
// process ch
}
feof() Explained
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā Understanding feof() ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā ā
ā feof() returns true AFTER you've tried to read past EOF. ā
ā It does NOT predict if the next read will reach EOF. ā
ā ā
ā Common WRONG pattern: ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā while (!feof(fp)) { ā ā
ā ā fscanf(fp, "%d", &n); // May fail ā ā
ā ā printf("%d\n", n); // Prints garbage! ā
ā ā } ā ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā
ā CORRECT pattern: ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā while (fscanf(fp, "%d", &n) == 1) { ā ā
ā ā printf("%d\n", n); ā ā
ā ā } ā ā
ā ā if (!feof(fp)) { ā ā
ā ā printf("Error reading file\n"); ā ā
ā ā } ā ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Distinguishing EOF from Error
#include <stdio.h>
void read_file(const char *filename) {
FILE *fp = fopen(filename, "r");
if (fp == NULL) return;
int ch;
while ((ch = fgetc(fp)) != EOF) {
putchar(ch);
}
/* Now check WHY loop ended */
if (feof(fp)) {
printf("\n[End of file reached normally]\n");
} else if (ferror(fp)) {
printf("\n[Error reading file]\n");
clearerr(fp); // Clear error indicator
}
fclose(fp);
}
Reading Entire File
Method 1: Read into Dynamically Allocated Buffer
#include <stdio.h>
#include <stdlib.h>
char *read_entire_file(const char *filename, long *out_size) {
FILE *fp = fopen(filename, "rb");
if (fp == NULL) return NULL;
/* Get file size */
fseek(fp, 0, SEEK_END);
long size = ftell(fp);
rewind(fp);
/* Allocate buffer (+1 for null terminator if text) */
char *buffer = malloc(size + 1);
if (buffer == NULL) {
fclose(fp);
return NULL;
}
/* Read entire file */
long read = fread(buffer, 1, size, fp);
if (read != size) {
free(buffer);
fclose(fp);
return NULL;
}
buffer[size] = '\0'; // Null terminate for text
if (out_size) *out_size = size;
fclose(fp);
return buffer;
}
int main() {
long size;
char *content = read_entire_file("example.txt", &size);
if (content) {
printf("File size: %ld bytes\n", size);
printf("Content:\n%s\n", content);
free(content);
}
return 0;
}
Method 2: Read Line by Line into Array
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_LINES 1000
#define MAX_LINE_LENGTH 256
int main() {
FILE *fp = fopen("text.txt", "r");
if (fp == NULL) return 1;
char *lines[MAX_LINES];
int line_count = 0;
char buffer[MAX_LINE_LENGTH];
while (fgets(buffer, sizeof(buffer), fp) && line_count < MAX_LINES) {
lines[line_count] = strdup(buffer); // Duplicate the string
if (lines[line_count] == NULL) break;
line_count++;
}
fclose(fp);
printf("Read %d lines:\n", line_count);
for (int i = 0; i < line_count; i++) {
printf("%d: %s", i + 1, lines[i]);
free(lines[i]); // Free each line
}
return 0;
}
Common Reading Patterns
Pattern 1: Process File Line by Line
void process_lines(const char *filename) {
FILE *fp = fopen(filename, "r");
if (fp == NULL) return;
char line[1024];
int line_num = 0;
while (fgets(line, sizeof(line), fp)) {
line_num++;
// Remove newline
line[strcspn(line, "\n")] = '\0';
// Skip empty lines
if (line[0] == '\0') continue;
// Skip comments
if (line[0] == '#') continue;
printf("Processing line %d: %s\n", line_num, line);
}
fclose(fp);
}
Pattern 2: Read Key-Value Configuration
void read_config(const char *filename) {
FILE *fp = fopen(filename, "r");
if (fp == NULL) return;
char line[256];
char key[128], value[128];
while (fgets(line, sizeof(line), fp)) {
// Skip comments and empty lines
if (line[0] == '#' || line[0] == '\n') continue;
// Parse key=value
if (sscanf(line, "%127[^=]=%127[^\n]", key, value) == 2) {
printf("Key: '%s', Value: '%s'\n", key, value);
}
}
fclose(fp);
}
Pattern 3: Read CSV Data
void read_csv(const char *filename) {
FILE *fp = fopen(filename, "r");
if (fp == NULL) return;
char line[1024];
// Skip header
fgets(line, sizeof(line), fp);
char name[50], city[50];
int age;
float salary;
while (fgets(line, sizeof(line), fp)) {
if (sscanf(line, "%49[^,],%d,%49[^,],%f",
name, &age, city, &salary) == 4) {
printf("%s, %d, %s, %.2f\n", name, age, city, salary);
}
}
fclose(fp);
}
Pattern 4: Read Records of Fixed Size
struct Record {
int id;
char data[100];
};
void read_records(const char *filename) {
FILE *fp = fopen(filename, "rb");
if (fp == NULL) return;
struct Record rec;
int count = 0;
while (fread(&rec, sizeof(struct Record), 1, fp) == 1) {
count++;
printf("Record %d: ID=%d, Data=%s\n", count, rec.id, rec.data);
}
printf("Total records: %d\n", count);
fclose(fp);
}
Error Handling While Reading
Comprehensive Error Handling
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
int read_file_safely(const char *filename) {
FILE *fp = fopen(filename, "r");
if (fp == NULL) {
fprintf(stderr, "Error opening %s: %s\n",
filename, strerror(errno));
return -1;
}
char buffer[256];
int line_count = 0;
while (fgets(buffer, sizeof(buffer), fp) != NULL) {
line_count++;
/* Check for read error mid-file */
if (ferror(fp)) {
fprintf(stderr, "Error reading file at line %d\n", line_count);
clearerr(fp);
break;
}
printf("%s", buffer);
}
/* Final status check */
if (ferror(fp)) {
fprintf(stderr, "Error occurred while reading\n");
fclose(fp);
return -1;
}
if (feof(fp)) {
printf("\n[Read %d lines successfully]\n", line_count);
}
fclose(fp);
return line_count;
}
Error Checking Functions
| Function | Purpose |
|---|---|
feof(fp) | Returns non-zero if EOF indicator is set |
ferror(fp) | Returns non-zero if error indicator is set |
clearerr(fp) | Clears both EOF and error indicators |
errno | System error number (set by failed operations) |
strerror(errno) | Human-readable error message |
Performance Considerations
Buffering Strategies
#include <stdio.h>
void optimize_reading(FILE *fp) {
/* Set larger buffer for better performance */
char buffer[65536]; // 64KB buffer
setvbuf(fp, buffer, _IOFBF, sizeof(buffer));
/* Now read operations are more efficient */
}
Reading Efficiency Comparison
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā Reading Performance ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā ā
ā SLOWEST ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā while ((ch = fgetc(fp)) != EOF) ā Character by char ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā¼ ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā while (fgets(buffer, 256, fp)) ā Line by line ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā¼ ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā fread(buffer, 1, 4096, fp) ā Block reading ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā¼ ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā fread(buffer, 1, file_size, fp) ā Whole file ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā FASTEST ā
ā ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Tips for Better Performance
- ā¢Use larger buffers when reading large files
- ā¢Read in blocks with
fread()for binary data - ā¢Minimize function calls - read more data at once
- ā¢Avoid repeated opens - cache file contents if needed
- ā¢Use appropriate mode - "rb" for binary is faster
Best Practices
1. Always Validate Input
/* Check filename */
if (filename == NULL || filename[0] == '\0') {
return -1;
}
/* Check file opened successfully */
FILE *fp = fopen(filename, "r");
if (fp == NULL) {
perror("Error");
return -1;
}
2. Use Appropriate Function
/* For character-by-character processing */
int ch;
while ((ch = fgetc(fp)) != EOF) { ... }
/* For line-by-line text */
char buffer[256];
while (fgets(buffer, sizeof(buffer), fp)) { ... }
/* For structured data */
while (fscanf(fp, "%d %s", &num, str) == 2) { ... }
/* For binary data */
while (fread(&record, sizeof(record), 1, fp) == 1) { ... }
3. Handle Errors Properly
if (ferror(fp)) {
fprintf(stderr, "Read error: %s\n", strerror(errno));
clearerr(fp);
}
4. Limit Buffer Sizes in fscanf
/* DANGEROUS */
fscanf(fp, "%s", buffer);
/* SAFE */
fscanf(fp, "%255s", buffer); // for buffer[256]
5. Always Close Files
FILE *fp = fopen("file.txt", "r");
if (fp != NULL) {
// ... use file ...
fclose(fp);
fp = NULL;
}
Summary
Quick Reference
| Task | Function | Example |
|---|---|---|
| Read character | fgetc() | ch = fgetc(fp) |
| Read line | fgets() | fgets(buf, size, fp) |
| Read formatted | fscanf() | fscanf(fp, "%d", &n) |
| Read binary | fread() | fread(buf, sz, n, fp) |
| Check EOF | feof() | if (feof(fp)) |
| Check error | ferror() | if (ferror(fp)) |
Reading Loop Patterns
/* fgetc pattern */
int ch;
while ((ch = fgetc(fp)) != EOF) { ... }
/* fgets pattern */
while (fgets(buffer, sizeof(buffer), fp) != NULL) { ... }
/* fscanf pattern */
while (fscanf(fp, format, ...) == expected_count) { ... }
/* fread pattern */
while (fread(&item, sizeof(item), 1, fp) == 1) { ... }
Common Mistakes to Avoid
- ā¢Using
charinstead ofintforfgetc()return - ā¢Using
while (!feof(fp))as loop condition - ā¢Not checking return values
- ā¢Not limiting buffer size in
fscanf() - ā¢Forgetting to remove newline from
fgets()input - ā¢Not closing files
- ā¢Not handling partial reads in
fread()
Practice Exercises Preview
- ā¢Read and display file contents using fgetc()
- ā¢Count words, lines, and characters in a file
- ā¢Read CSV data into an array of structures
- ā¢Implement a simple file viewer with line numbers
- ā¢Parse a configuration file with key-value pairs