File Opening and Closing
File Opening
The fopen() function is used to open a file, with the following prototype:
FILE *fopen(const char *filename, const char *mode);
filename: A pointer to a string containing the file name.mode: Specifies the file opening mode. Common modes include:
"r": Read-only mode; the file must exist."w": Write mode; if the file exists, it will be truncated; if it does not exist, a new file will be created."a": Append mode; the file pointer is positioned at the end of the file; if the file does not exist, a new file will be created."r+": Read-write mode; the file must exist."w+": Read-write mode; if the file exists, it will be truncated; if it does not exist, a new file will be created."a+": Read-write append mode; the file pointer is positioned at the end of the file; if the file does not exist, a new file will be created."rb","wb","ab","rb+","wb+","ab+": Same as above, but for binary files.
fopen() returns a pointer to a FILE structure upon successful opening, or NULL on failure.
Common Opening Modes Table
| Mode String | Description |
|---|---|
"r" | Read-only mode (text file): The file must exist, otherwise opening fails. |
"w" | Write-only mode (text file): Creates the file if it doesn’t exist; truncates if it does. |
"a" | Append mode (text file): Creates the file if it doesn’t exist; appends to the end if it does. |
"rb" | Read-only mode (binary file): Same as "r", but reads/writes in binary mode. |
"wb" | Write-only mode (binary file): Same as "w", but reads/writes in binary mode. |
"ab" | Append mode (binary file): Same as "a", but reads/writes in binary mode. |
"r+" | Read-write mode (text file): The file must exist, supports reading and writing. |
"w+" | Read-write mode (text file): Creates the file if it doesn’t exist; truncates if it does, supports reading and writing. |
"a+" | Read-write append mode (text file): Creates the file if it doesn’t exist; appends to the end if it does, supports reading and writing. |
"rb+" | Read-write mode (binary file): Same as "r+", but reads/writes in binary mode. |
"wb+" | Read-write mode (binary file): Same as "w+", but reads/writes in binary mode. |
"ab+" | Read-write append mode (binary file): Same as "a+", but reads/writes in binary mode. |
Example: Opening a File
#include <stdio.h>
#include <stdlib.h>
int main() {
FILE* fp;
// Open text file (read-only)
fp = fopen("data.txt", "r");
if (fp == NULL) { // Check if opening succeeded
perror("Failed to open file"); // Output error message (e.g., "No such file or directory")
exit(EXIT_FAILURE); // Exit program
}
printf("File opened (read-only mode)\n");
// Open binary file (read-write, create if not exists)
FILE* bin_fp = fopen("data.bin", "wb+");
if (bin_fp == NULL) {
perror("Failed to open binary file");
fclose(fp); // Close already opened file (avoid resource leak)
exit(EXIT_FAILURE);
}
printf("Binary file opened (read-write mode)\n");
// Close files (detailed in later sections)
fclose(fp);
fclose(bin_fp);
return 0;
}
Notes
- Mode Selection: Binary files (e.g., images, executables) must use
"rb","wb", etc., to avoid garbled data or corruption. - File Existence:
"r","rb"require the file to exist, otherwisefopenreturnsNULL;"w","wb"overwrite existing files—use with caution. - Error Handling: Always check the return value of
fopen(NULLindicates failure), and useperrororstrerror(errno)to locate the specific error.
File Closing
The fclose() function is used to close an already opened file, with the following prototype:
int fclose(FILE *stream);
stream: A pointer to theFILEstructure returned byfopen().fclose()returns0on success andEOF(usually defined as-1) on failure.
Example Code Below is a simple example using fopen() and fclose():
#include <stdio.h>
int main() {
FILE *file;
file = fopen("example.txt", "w"); // Open or create a file named example.txt in write mode
if (file == NULL) {
printf("Error opening file.\n");
return 1;
}
fprintf(file, "Hello, world!\n"); // Write text to the file
fclose(file); // Close the file
return 0;
}
In this example, we first attempt to open or create a file named example.txt in write mode ("w"). If the file is successfully opened, we write some text to it and then close the file.
Notes
- Always call
fclose()after using a file to ensure all buffered data is written to disk and resources are released. - Forgetting to close a file may result in data loss or program resource leaks.
- When a file is opened in write mode (
"w"), existing content is cleared; in append mode ("a"), the file pointer is positioned at the end without clearing content.
File Read and Write Operations
File reading and writing are the core of file operations. C provides multiple functions, including byte-oriented fread/fwrite and format-oriented fscanf/fprintf.
Byte-Oriented Read/Write (fread/fwrite)
fread: Read Binary Data
fread is used to read a specified number of bytes of binary data from a file, with the following prototype:
size_t fread(void* ptr, size_t size, size_t nmemb, FILE* stream);
ptr: Memory buffer to store the read data (pointer).size: Size in bytes of each data element (e.g.,sizeof(int)).nmemb: Number of elements to read.stream: File pointer (FILE*).- Return value: Number of elements actually read (if less than
nmemb, may have reached end of file or an error occurred).
Example: Reading a Binary File
#include <stdio.h>
#include <stdlib.h>
int main() {
FILE* bin_fp = fopen("data.bin", "rb");
if (bin_fp == NULL) { perror("Open failed"); exit(EXIT_FAILURE); }
// Read 10 integers (each 4 bytes)
int buffer[10];
size_t read_count = fread(buffer, sizeof(int), 10, bin_fp);
if (read_count < 10) {
if (feof(bin_fp)) {
printf("Reached end of file, actually read %zu integers\n", read_count);
} else if (ferror(bin_fp)) {
perror("Read error");
}
} else {
printf("Successfully read 10 integers\n");
}
fclose(bin_fp);
return 0;
}
fwrite: Write Binary Data
fwrite is used to write a specified number of bytes of binary data to a file, with the following prototype:
size_t fwrite(const void* ptr, size_t size, size_t nmemb, FILE* stream);
- Parameter meanings: Similar to
fread,ptris the buffer containing data to write. - Return value: Number of elements actually written (usually equals
nmembunless an error occurs).
Example: Writing a Binary File
#include <stdio.h>
#include <stdlib.h>
int main() {
FILE* bin_fp = fopen("data.bin", "wb");
if (bin_fp == NULL) { perror("Open failed"); exit(EXIT_FAILURE); }
int data[] = {1, 2, 3, 4, 5};
size_t write_count = fwrite(data, sizeof(int), 5, bin_fp);
if (write_count != 5) {
perror("Write failed");
fclose(bin_fp);
exit(EXIT_FAILURE);
}
printf("Successfully wrote 5 integers\n");
fclose(bin_fp);
return 0;
}
Notes
- Binary Mode:
fread/fwritemust be used with"rb","wb", etc., to avoid issues from newline conversion (e.g.,\r\non Windows). - Buffer Alignment: Ensure the memory buffer pointed to by
ptris aligned tosize(e.g., address should be a multiple of 4 when readingint). - Partial Read/Write: Return value may be less than
nmemb(e.g., end of file or disk full); check return value to handle exceptions.
Format-Oriented Read/Write (fscanf/fprintf)
fprintf: Formatted Text Output
fprintf is used to write formatted text data to a file (similar to printf, but outputs to a file), with the following prototype:
int fprintf(FILE* stream, const char* format, ...);
- Parameter meanings:
formatis the format string (e.g.,"%d %s %.2f\n"), followed by variables to format. - Return value: Number of characters successfully written (negative on error).
Example: Writing to a Text File
#include <stdio.h>
#include <stdlib.h>
int main() {
FILE* txt_fp = fopen("data.txt", "w");
if (txt_fp == NULL) { perror("Open failed"); exit(EXIT_FAILURE); }
int age = 20;
char name[] = "Zhang San";
float score = 85.5;
// Write formatted text
fprintf(txt_fp, "Name: %s, Age: %d, Score: %.2f\n", name, age, score);
fclose(txt_fp);
return 0;
}
fscanf: Formatted Text Input
fscanf is used to read formatted text data from a file (similar to scanf, but input comes from a file), with the following prototype:
int fscanf(FILE* stream, const char* format, ...);
- Parameter meanings:
formatis the format string (e.g.,"%d %s %f"), followed by pointers to store the results. - Return value: Number of input items successfully matched and assigned (
EOFon end of file or error).
Example: Reading from a Text File
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
FILE* txt_fp = fopen("data.txt", "r");
if (txt_fp == NULL) { perror("Open failed"); exit(EXIT_FAILURE); }
char name[50];
int age;
float score;
// Read formatted text
int ret = fscanf(txt_fp, "Name: %s, Age: %d, Score: %f\n", name, &age, &score);
if (ret == EOF) {
if (feof(txt_fp)) {
printf("File is empty or read failed\n");
} else if (ferror(txt_fp)) {
perror("Read error");
}
} else if (ret == 3) { // Successfully matched 3 items
printf("Read successful: Name=%s, Age=%d, Score=%.2f\n", name, age, score);
} else {
printf("Partial match (%d items), remaining data may have format errors\n", ret);
}
fclose(txt_fp);
return 0;
}
Notes
- Text Mode:
fprintf/fscanfmust be used with"r","w", etc., to avoid format errors due to newline conversion. - Buffer Flush: After writing, call
fflush(stream)to flush the output buffer if immediate disk write is needed (fcloseflushes automatically). - String Length: When using
%sto read strings, ensure the target buffer is large enough (avoid buffer overflow), or use%nsto limit length (e.g.,%49sreads at most 49 characters).
File Positioning (fseek/ftell)
File positioning controls the file pointer’s position (offset from start, current position, or end), enabling random access (e.g., jumping to the middle of a file).
File Positioning Functions (fseek/ftell)
fseek: Move File Pointer
fseek moves the file pointer by a specified offset from a reference position, with the following prototype:
int fseek(FILE* stream, long int offset, int whence);
offset: Offset in bytes (can be positive or negative).whence: Reference position (enumerated values):SEEK_SET: From the beginning of the file (offset starts at 0).SEEK_CUR: From the current file pointer position.SEEK_END: From the end of the file (negative offset moves forward).
- Return value:
0on success, non-zero on failure (e.g., file not opened or offset out of range).
Example: Moving File Pointer
#include <stdio.h>
#include <stdlib.h>
int main() {
FILE* fp = fopen("data.txt", "r");
if (fp == NULL) { perror("Open failed"); exit(EXIT_FAILURE); }
// Move to the beginning of the file (equivalent to rewind(fp))
fseek(fp, 0, SEEK_SET);
// Move to the end of the file (to get file size)
fseek(fp, 0, SEEK_END);
long file_size = ftell(fp); // See section 3.1.2
printf("File size: %ld bytes\n", file_size);
// Offset 10 bytes from the beginning
fseek(fp, 10, SEEK_SET);
char buffer[11];
fgets(buffer, sizeof(buffer), fp); // Read 10 characters starting from byte 10
printf("Content after 10-byte offset: %s\n", buffer);
fclose(fp);
return 0;
}
ftell: Get Current File Pointer Position
ftell returns the current file pointer position relative to the start of the file, with the following prototype:
long int ftell(FILE* stream);
- Return value: Current file pointer position in bytes (
-1Lon error).
Example: Getting File Size
FILE* fp = fopen("data.txt", "r");
if (fp == NULL) { /* Error handling */ }
fseek(fp, 0, SEEK_END); // Move to end of file
long size = ftell(fp); // Offset is now the file size
printf("File size: %ld bytes\n", size);
fseek(fp, 0, SEEK_SET); // Move back to start
Notes
- Binary Mode:
fseek/ftellare more reliable in binary mode (text mode may cause incorrect offsets due to newline conversion). - Large File Support: On 32-bit systems,
longmay not represent offsets beyond 2GB (usefseeko/ftellowithoff_ttype). - Error Checking: If
fseekfails (e.g., offset too large), the file pointer position may be undefined; useferrorto check for errors.
Other File Positioning Functions
rewind: Reset File Pointer to Beginning
rewind is a macro for fseek(fp, 0, SEEK_SET), quickly resetting the file pointer to the start:
void rewind(FILE* stream);
Example:
FILE* fp = fopen("data.txt", "r");
// ... (read some data)
rewind(fp); // Pointer back to start, can reread
fgetpos/fsetpos: Save/Restore File Position
Used for large files or when saving file position is needed (supports fpos_t type):
int fgetpos(FILE* stream, fpos_t* pos); // Save current position to pos
int fsetpos(FILE* stream, const fpos_t* pos); // Restore position from pos
Example:
FILE* fp = fopen("large_file.bin", "rb");
fpos_t pos;
// Save current position
fgetpos(fp, &pos);
// Move to end of file
fseek(fp, 0, SEEK_END);
// Restore previous position
fsetpos(fp, &pos);
File Upload and Download (Network Programming Basics)
File upload and download involve network programming and require the Socket API. The C standard library does not directly support network operations, but they can be implemented using system-provided Socket interfaces. Below is a simple file transfer example based on the TCP protocol.
Network Programming Basic Concepts
- Socket: Endpoint for network communication, created with
socket(). - IP Address: Identifies a host on the network (e.g.,
192.168.1.1). - Port Number: Identifies a service on the host (e.g., HTTP default port 80).
- TCP: Connection-oriented reliable transmission protocol (suitable for file transfer).
File Upload Example (Client to Server)
Server-Side Code (Receive File)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#define PORT 8080
#define BUFFER_SIZE 1024
int main() {
int server_fd, new_socket;
struct sockaddr_in address;
int opt = 1;
int addrlen = sizeof(address);
FILE* fp;
// Create Socket
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
perror("Socket creation failed");
exit(EXIT_FAILURE);
}
// Set Socket options (allow port reuse)
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) {
perror("setsockopt failed");
exit(EXIT_FAILURE);
}
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(PORT);
// Bind port
if (bind(server_fd, (struct sockaddr*)&address, sizeof(address)) < 0) {
perror("bind failed");
exit(EXIT_FAILURE);
}
// Listen for connections
if (listen(server_fd, 3) < 0) {
perror("listen failed");
exit(EXIT_FAILURE);
}
printf("Server listening on port %d...\n", PORT);
// Accept client connection
if ((new_socket = accept(server_fd, (struct sockaddr*)&address, (socklen_t*)&addrlen)) < 0) {
perror("accept failed");
exit(EXIT_FAILURE);
}
// Receive filename
char filename[256];
recv(new_socket, filename, sizeof(filename), 0);
printf("Receiving file: %s\n", filename);
// Open file (write mode)
fp = fopen(filename, "wb");
if (fp == NULL) {
perror("Failed to open file");
close(new_socket);
exit(EXIT_FAILURE);
}
// Receive file data
char buffer[BUFFER_SIZE];
ssize_t bytes_received;
while ((bytes_received = recv(new_socket, buffer, BUFFER_SIZE, 0)) > 0) {
fwrite(buffer, 1, bytes_received, fp); // Write to file
}
if (bytes_received < 0) {
perror("Failed to receive data");
} else {
printf("File reception completed\n");
}
// Close resources
fclose(fp);
close(new_socket);
close(server_fd);
return 0;
}
Client-Side Code (Upload File)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#define PORT 8080
#define BUFFER_SIZE 1024
int main() {
int sock = 0;
struct sockaddr_in serv_addr;
FILE* fp;
char buffer[BUFFER_SIZE];
ssize_t bytes_read;
// Create Socket
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
perror("Socket creation failed");
exit(EXIT_FAILURE);
}
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(PORT);
// Convert IP address (assuming server IP is 127.0.0.1)
if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0) {
perror("Invalid IP address");
exit(EXIT_FAILURE);
}
// Connect to server
if (connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {
perror("Connection failed");
exit(EXIT_FAILURE);
}
// Send filename
char filename[] = "test.txt";
send(sock, filename, strlen(filename), 0);
// Open file (read mode)
fp = fopen(filename, "rb");
if (fp == NULL) {
perror("Failed to open file");
close(sock);
exit(EXIT_FAILURE);
}
// Send file data
while ((bytes_read = fread(buffer, 1, BUFFER_SIZE, fp)) > 0) {
send(sock, buffer, bytes_read, 0); // Send data
}
if (bytes_read < 0) {
perror("Failed to read data");
} else {
printf("File upload completed\n");
}
// Close resources
fclose(fp);
close(sock);
return 0;
}
Compile and Run
- Compile server:
gcc server.c -o server - Compile client:
gcc client.c -o client - Run server:
./server - Run client:
./client
File Download Example (Server to Client)
File download logic is similar to upload, just reverse the read/write direction on client and server:
- Server reads file data and sends via
send. - Client receives data via
recvand writes to local file.
Summary and Best Practices
Key Summary
- File Open/Close: Always check
fopenreturn value to avoid resource leaks. - File Read/Write: Use
fread/fwritefor binary files,fscanf/fprintffor text files; match with appropriate mode. - File Positioning:
fseek/ftellfor random access, more reliable in binary mode. - Network File Transfer: Based on TCP protocol, use Socket API for client-server communication.
Best Practices
- Error Handling: Check return values after all file and network operations, handle errors promptly (e.g.,
perror). - Resource Release: Ensure files (
fclose) and sockets (close) are closed to avoid resource leaks. - Buffer Management: Use
fflushto flush output buffer to prevent data loss. - Large File Support: On 64-bit systems, use
fseeko/ftellowithoff_ttype to handle files over 2GB.
By mastering these skills, developers can efficiently implement file operations and network file transfer in C, meeting various persistent data management needs.



