File Handling
Opening, reading from, writing to, and closing files in C. Using functions like `fopen`, `fclose`, `fread`, `fwrite`, `fprintf`, and `fscanf`.
Reading from Files in C: fread and fscanf
C provides powerful functions for reading data from files. This document explains how to use fread
for reading binary data and fscanf
for reading formatted text data.
fread: Reading Binary Data
The fread
function is used to read a block of data from a file as a sequence of bytes. This is particularly useful for reading binary files, where data is not necessarily represented in human-readable text format.
Syntax
size_t fread(void *ptr, size_t size, size_t count, FILE *stream);
Parameters
ptr
: A pointer to the memory location where the data will be stored. This should be a buffer large enough to hold the expected data.size
: The size of each element to be read, in bytes.count
: The number of elements to be read.stream
: A pointer to theFILE
object representing the opened file.
Return Value
fread
returns the number of elements successfully read. This may be less than count
if an error occurs (e.g., end-of-file is reached). It's crucial to check the return value to ensure that the read operation was successful.
Example: Reading an Array of Integers
This example demonstrates how to read an array of integers from a binary file.
#include <stdio.h>
#include <stdlib.h>
int main() {
FILE *fp;
int data[5];
size_t num_read;
fp = fopen("data.bin", "rb"); // Open in binary read mode
if (fp == NULL) {
perror("Error opening file");
return 1;
}
num_read = fread(data, sizeof(int), 5, fp);
if (num_read == 5) {
printf("Read %zu elements successfully:\n", num_read);
for (int i = 0; i < 5; i++) {
printf("data[%d] = %d\n", i, data[i]);
}
} else {
fprintf(stderr, "Error reading data: Read only %zu elements\n", num_read);
}
fclose(fp);
return 0;
}
Explanation:
- The file "data.bin" is opened in binary read mode ("rb").
fread
attempts to read 5 elements, each of sizesizeof(int)
, from the file into thedata
array.- The return value
num_read
is checked to ensure that all 5 elements were read successfully.
Creating the Binary Data File (data.bin)
The above code requires a binary file named 'data.bin'. You can create this file using a separate C program, such as:
#include <stdio.h>
#include <stdlib.h>
int main() {
FILE *fp;
int data[] = {10, 20, 30, 40, 50};
size_t num_written;
fp = fopen("data.bin", "wb"); // Open in binary write mode
if (fp == NULL) {
perror("Error opening file");
return 1;
}
num_written = fwrite(data, sizeof(int), 5, fp);
if (num_written == 5) {
printf("Wrote %zu elements successfully to data.bin\n", num_written);
} else {
fprintf(stderr, "Error writing data: Wrote only %zu elements\n", num_written);
}
fclose(fp);
return 0;
}
fscanf: Reading Formatted Text Data
The fscanf
function is used to read formatted data from a file, similar to how scanf
reads from standard input. It parses the input stream according to a format string.
Syntax
int fscanf(FILE *stream, const char *format, ...);
Parameters
stream
: A pointer to theFILE
object representing the opened file.format
: A format string that specifies the expected format of the input. It is a string containing format specifiers (e.g.,%d
for integers,%f
for floating-point numbers,%s
for strings)....
: A variable number of arguments, each of which is a pointer to a variable where the scanned data will be stored.
Return Value
fscanf
returns the number of input items successfully matched and assigned. This number can be 0 in the event of an early matching failure. It returns EOF
if an error occurs before any input items are successfully converted.
Example: Reading Name, Age, and Salary
This example demonstrates how to read a name (string), age (integer), and salary (float) from a text file.
#include <stdio.h>
#include <stdlib.h>
int main() {
FILE *fp;
char name[50];
int age;
float salary;
int num_read;
fp = fopen("employee.txt", "r"); // Open in read mode
if (fp == NULL) {
perror("Error opening file");
return 1;
}
num_read = fscanf(fp, "%49s %d %f", name, &age, &salary); //Limit string read to prevent buffer overflow
if (num_read == 3) {
printf("Name: %s\n", name);
printf("Age: %d\n", age);
printf("Salary: %.2f\n", salary);
} else {
fprintf(stderr, "Error reading data: Read only %d elements\n", num_read);
}
fclose(fp);
return 0;
}
Explanation:
- The file "employee.txt" is opened in read mode ("r").
fscanf
reads a string (up to 49 characters), an integer, and a float from the file.- The format string
"%49s %d %f"
specifies the expected format of the input. The%49s
limits the string read to 49 characters to avoid buffer overflows (plus a null terminator). - The addresses of the variables (
&age
,&salary
) are passed tofscanf
so that it can store the read values. - The return value
num_read
is checked to ensure that all three values were read successfully.
Creating the Text Data File (employee.txt)
The above code requires a text file named 'employee.txt'. This file would need to contain the data in the format expected by `fscanf`. An example:
JohnDoe 30 50000.50
Reading Different Datatypes
Both fread
and fscanf
can be used to read different data types. The key differences lie in how they interpret the data:
fread
fread
treats the data as a raw sequence of bytes. To read different data types, you need to specify the correct size
parameter to match the size of the data type.
int
: Usesizeof(int)
for thesize
parameter.float
: Usesizeof(float)
for thesize
parameter.double
: Usesizeof(double)
for thesize
parameter.char
: Usesizeof(char)
(which is always 1) for thesize
parameter. For strings, remember to allocate enough space for the null terminator.- Structures: Use
sizeof(struct YourStruct)
to read entire structures at once.
fscanf
fscanf
interprets the data based on the format string. You need to use the appropriate format specifier for each data type.
int
: Use%d
.long int
: Use%ld
.float
: Use%f
.double
: Use%lf
(note the 'l' prefix).char
: Use%c
.char*
(string): Use%s
. Important: Always specify a maximum field width to prevent buffer overflows (e.g.,%49s
). Do not use the unmodified `%s` specifier unless you're absolutely sure the input string length will never exceed the buffer size.
Error Handling
It's crucial to check the return values of both fread
and fscanf
to handle potential errors. Some common errors and how to address them:
- End-of-file (EOF): If
fread
reads fewer elements than requested, orfscanf
returns a value less than the expected number of matches, it might indicate the end of the file has been reached. - File not found: Ensure that the file exists and the program has the necessary permissions to access it. The
fopen
function will returnNULL
if it cannot open the file. - Incorrect format: If
fscanf
encounters data that does not match the format string, it will stop reading and return the number of successfully matched items up to that point. - Buffer overflows: When reading strings with
fscanf
, always use a maximum field width (e.g.,%49s
) to prevent writing beyond the bounds of the buffer. For `fread`, ensure your buffer size is sufficient.
Choosing between fread and fscanf
The choice between fread
and fscanf
depends on the nature of the data you are reading and the requirements of your application.
- Use
fread
when:- Reading binary data.
- Reading data where the exact format is not known or is irrelevant.
- Reading large blocks of data efficiently.
- Use
fscanf
when:- Reading formatted text data.
- Reading data where the format is well-defined and you need to parse specific values.
- Reading data that is human-readable.
Example: Reading and Writing structures with `fread`
This example shows how to read and write structure to a file using `fread` and `fwrite`
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
int id;
char name[50];
float salary;
} Employee;
int main() {
FILE *fp;
Employee empWrite = {123, "Alice Smith", 60000.0f};
Employee empRead;
size_t num_written, num_read;
// Write Employee struct to file
fp = fopen("employee.dat", "wb");
if (fp == NULL) {
perror("Error opening file for writing");
return 1;
}
num_written = fwrite(&empWrite, sizeof(Employee), 1, fp);
if (num_written != 1) {
fprintf(stderr, "Error writing to file\n");
fclose(fp);
return 1;
}
fclose(fp);
// Read Employee struct from file
fp = fopen("employee.dat", "rb");
if (fp == NULL) {
perror("Error opening file for reading");
return 1;
}
num_read = fread(&empRead, sizeof(Employee), 1, fp);
if (num_read != 1) {
fprintf(stderr, "Error reading from file\n");
fclose(fp);
return 1;
}
fclose(fp);
printf("Read Employee Data:\n");
printf("ID: %d\n", empRead.id);
printf("Name: %s\n", empRead.name);
printf("Salary: %.2f\n", empRead.salary);
return 0;
}