Data in memory disappears when the program ends. Files are data structures organized on external storage, which allow information to be kept between runs and exchanged between applications.
1Lab objectives
- Classifying files by content, access, and the type of operations allowed
- Correctly opening and closing a file, with a success check
- Choosing the right opening mode
- Reading and writing text and binary files
- Positioning within a file for direct access
2File classification
| Criterion | Categories | Description |
|---|---|---|
| By content | text files | characters organized in lines; end of line marked by <CR><LF> |
| typed files | records of a certain type, elementary or structured | |
| untyped files | binary files, raw sequences of bytes | |
| By access | sequential access | information is processed only in record order |
| direct access | components can be processed in any order, after positioning | |
| By operations | input | reading only |
| output | writing only | |
| input/output | both - information can be updated |
The end markerThe end of a file is signaled by a special marker called
EOF (end of file). The reading functions return it to indicate that no more data is
available.
3Opening and closing
Every transfer is preceded by the opening operation, which creates a stream and
associates it with the file. A variable of type FILE * is used, and a buffer zone
through which the transfers happen is allocated automatically.
the correct pattern
FILE *f = fopen("data.txt", "r"); // 1. opening
if (f == NULL) { // 2. mandatory CHECK
printf("Cannot open the file!\n");
return 1;
}
/* ... reading or writing ... */ // 3. processing
fclose(f); // 4. closing
Why closing is mandatoryWritten data sits in a buffer zone and only
reaches the disk once it fills up or on
fclose. If the program ends without closing,
the last data written can be lost. In addition, the number of files open at once is limited.| Function | Role |
|---|---|
fopen(name, mode) | opens the file; returns NULL on failure |
fclose(f) | flushes the buffer and closes the file |
feof(f) | tests whether the end of the file has been reached |
ferror(f) | tests whether an error has occurred |
fflush(f) | forces the buffer to be written to disk |
remove(name) | deletes the file |
rename(old, new) | renames the file |
4Opening modes
| Mode | Meaning | If the file exists | If it does not exist |
|---|---|---|---|
"r" | reading | opened from the start | error (NULL) |
"w" | writing | all contents are erased! | created |
"a" | appending | writing happens at the end | created |
"r+" | reading and writing | opened from the start | error |
"w+" | reading and writing | all contents are erased | created |
"a+" | reading and appending | writing happens at the end | created |
The letter b is added for binary files ("rb", "wb",
"ab") and t for text, which is the default mode.
The most dangerous mode is "w"Opening an existing file with
"w" instantly erases all of its contents, even if nothing is written afterward. To
append to an existing file, use "a".5Operations on text files
| Level | Reading | Writing |
|---|---|---|
| Character | fgetc(f), getc(f) | fputc(c, f), putc(c, f) |
| Line / string | fgets(s, n, f) | fputs(s, f) |
| Formatted | fscanf(f, "...", ...) | fprintf(f, "...", ...) |
Correspondence with the console functionsEvery file function has a
console equivalent:
fprintf ↔ printf, fscanf ↔
scanf, fgets ↔ gets. The difference is the first
parameter, which indicates the stream. printf(...) is equivalent to
fprintf(stdout, ...).Watch out for feofThe
feof function becomes true only
after a failed read, not before. The loop
while (!feof(f)) { fscanf(...); ... } processes the last record twice. Correct:
while (fscanf(f, "%d", &x) == 1) { ... }6Operations on binary files
prototypes
size_t fread (void *p, size_t size, size_t nr, FILE *f); size_t fwrite(const void *p, size_t size, size_t nr, FILE *f); // p - the address of the memory zone // size - the size of one record, in bytes // nr - how many records are transferred // f - the stream // returns the number of records actually transferred
| Aspect | Text file | Binary file |
|---|---|---|
| Content | readable characters | exact copy of the in-memory representation |
| The number 12345 | 5 characters (5 bytes) | 4 bytes (an int) |
| Manual editing | possible in any editor | practically impossible |
| Speed | slower (conversions) | fast, no conversions |
| Portability | good | limited (endianness, padding) |
7Direct access - positioning within a file
| Function | Role |
|---|---|
fseek(f, offset, origin) | moves the position indicator |
ftell(f) | returns the current position, in bytes |
rewind(f) | returns to the start of the file |
| Origin constant | Reference point |
|---|---|
SEEK_SET | the start of the file |
SEEK_CUR | the current position |
SEEK_END | the end of the file |
Reading record number n
fseek(f, n * sizeof(Record), SEEK_SET); followed by fread(...). This is
the major advantage of binary files with fixed-size records: any element is accessed directly,
with no need to scan the whole file.8Source code
text_write.c - creating and appending
#include <stdio.h>
int main(void)
{
FILE *f = fopen("data.txt", "w"); // WARNING: erases existing contents
if (f == NULL) {
printf("Cannot create the file!\n");
return 1;
}
fprintf(f, "John Smith;20;9.15\n");
fprintf(f, "Mary Johnson;21;8.70\n");
fprintf(f, "Radu Georgescu;20;9.60\n");
fclose(f);
printf("File created.\n");
// appending at the end, without erasing the contents
f = fopen("data.txt", "a");
if (f) {
fprintf(f, "Ana Dumitrescu;22;9.85\n");
fclose(f);
printf("Record added.\n");
}
return 0;
}
text_read.c - correct traversal
#include <stdio.h>
int main(void)
{
FILE *f = fopen("data.txt", "r");
if (f == NULL) {
printf("The file does not exist!\n");
return 1;
}
char line[200];
int nrLines = 0;
// correct version: the condition is precisely the success of the read
while (fgets(line, sizeof(line), f) != NULL) {
nrLines++;
printf("%3d: %s", nrLines, line);
}
printf("\nTotal %d lines\n", nrLines);
fclose(f);
// formatted reading, field by field
f = fopen("data.txt", "r");
char name[50];
int age;
float average;
printf("\nStructured reading:\n");
while (fscanf(f, "%49[^;];%d;%f\n", name, &age, &average) == 3) {
printf(" %-20s %3d years average %.2f\n", name, age, average);
}
fclose(f);
return 0;
}
binary.c - writing and reading structures
#include <stdio.h>
typedef struct {
char name[50];
int age;
float average;
} Student;
int main(void)
{
Student group[3] = {
{"John Smith", 20, 9.15f},
{"Mary Johnson", 21, 8.70f},
{"Radu Georgescu",20, 9.60f}
};
// binary write: the whole array, in a single call
FILE *f = fopen("students.dat", "wb");
if (!f) return 1;
fwrite(group, sizeof(Student), 3, f);
fclose(f);
// reading record number 1 (the second one), WITHOUT reading the first
f = fopen("students.dat", "rb");
if (!f) return 1;
Student one;
fseek(f, 1 * sizeof(Student), SEEK_SET); // direct positioning
fread(&one, sizeof(Student), 1, f);
printf("Record 1: %s, %d years, average %.2f\n",
one.name, one.age, one.average);
// the size of the file
fseek(f, 0, SEEK_END);
long size = ftell(f);
printf("File size: %ld bytes = %ld records\n",
size, size / (long)sizeof(Student));
fclose(f);
return 0;
}
copy.c - copying a file, byte by byte
#include <stdio.h>
int main(void)
{
FILE *source = fopen("data.txt", "rb");
if (!source) { printf("The source is missing\n"); return 1; }
FILE *dest = fopen("copy.txt", "wb");
if (!dest) { fclose(source); return 1; }
char buffer[1024];
size_t bytesRead;
long total = 0;
// copying in blocks, much faster than byte by byte
while ((bytesRead = fread(buffer, 1, sizeof(buffer), source)) > 0) {
fwrite(buffer, 1, bytesRead, dest);
total += bytesRead;
}
printf("Copied %ld bytes\n", total);
fclose(source);
fclose(dest);
return 0;
}
9Code workshop
The boxes below have their own file system, which exists only on the page. Files written by one program can be read right afterward by the same program.
Try thisChange the mode from
"w" to "a"
(append) and run twice: you will see the lines being added at the end instead of replacing the
file.Try thisThe file
story.txt already exists in this box's
file system.10Work tasks
- Create a text file with student data and then read it line by line.
- Open an existing file with mode
"w"and observe what happens to its contents. - Add new records using mode
"a", without losing the old data. - Count the lines, words, and characters in a text file.
- Write an array of structures to a binary file and read only the third record.
- Compare the size of the same information saved in text format and in binary format.
- Implement file copying using a 1024-byte buffer.
- Test the difference between
while (!feof(f))andwhile (fscanf(...) == n).
11Extended application
ExtensionImplement a persistent phone book, with a binary file of
fixed-size records: adding, searching by name, modifying an existing record (through
fseek and rewriting in place, without recreating the file), and logical deletion
through an indicator field. Add a "compaction" function that physically removes the records
marked as deleted.