Linux File IO and Standard IO Explained: From Concepts to Practice
I. Core Concepts: File IO vs Standard IO
1.1 Basic Definitions
- File IO (System Calls) : Low-level system functions provided by the operating system for users to manipulate files (e.g.,
open/read/write/close). It interacts directly with the kernel and is also called "low-level IO". - Standard IO (C Library Functions) : File operation functions encapsulated by the C standard library (e.g.,
fopen/fread/fwrite/fclose). It calls File IO at the underlying layer and features better cross-platform compatibility, also known as "high-level IO".
1.2 Underlying Relationship and Core Differences
C library functions are wrappers around system calls: Standard IO adds a user-mode buffer to improve efficiency, while File IO has no buffer (direct kernel-mode operations). The core differences between the two are summarized below:
| Feature | File IO (System Call) | Standard IO (C Library) |
|---|---|---|
| Operation Interface | open/read/write/close/lseek |
fopen/fread/fwrite/fclose/fseek |
| Identifier Type | File descriptor (int; e.g., 0/1/2 for stdin/stdout/stderr respectively) | FILE stream pointer (FILE*) |
| Buffering Mechanism | No buffer (direct kernel operations) | With user-mode buffer (reduces system call frequency) |
| Cross-platform Support | OS-dependent (e.g., Linux-specific) | Cross-platform (compliant with C standards) |
| Applicable Scenarios | Device files, real-time requirements | Regular files, general scenarios pursuing efficiency |
| Permission Control | Specified directly when opening (e.g., 0666) | Depends on fopen mode (e.g., "r+") |
II. Practical File IO (System Call)
File IO is a low-level interface provided by the Linux kernel, featuring no buffering and powerful functions. It is suitable for scenarios with device operations or high real-time requirements. Core functions include open/read/write/lseek/close.
2.1 open: Open or Create a File
The open function is used to open or create a file, returning a unique file descriptor (fd). It returns -1 on failure.
c
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
int open(const char *pathname, int flags, int mode);
- Parameter Description :
pathname: File path/name;flags: Open mode (core:O_RDONLY(read-only)/O_WRONLY(write-only)/O_RDWR(read-write); extensions:O_CREAT(create if not exists)/O_TRUNC(truncate content)/O_APPEND(append mode));mode: File permission when creating (e.g., 0666, meaning readable and writable for all users).
Practical Example (01open.c): Create and truncate the 1.txt file
c
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <stdio.h>
int main(int argc, char **argv)
{
// Open in write-only mode, create if not exists, truncate if exists, with 0666 permission
int fd = open("1.txt", O_WRONLY| O_CREAT|O_TRUNC,0666);
if(-1 == fd) // Error handling: -1 indicates failure to get file descriptor
{
fprintf(stderr,"open error\n");
return 1;
}
return 0;
}
2.2 read/write: Read and Write Files
File reading and writing in File IO are implemented via read (read) and write (write), which operate directly on byte streams without buffering.
(1) write: Write to a File
c
ssize_t write(int fd, const void *buf, size_t count);
fd: Target file descriptor;buf: Buffer containing data to be written;count: Number of valid bytes to be written;- Return value: Number of bytes actually written on success, -1 on failure.
Practical Example (04write.c): Write "hello" to 1.txt
c
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
int main(int argc, char **argv)
{
int fd = open("1.txt", O_WRONLY| O_CREAT|O_TRUNC,0666);
if(-1 == fd)
{
fprintf(stderr,"open error\n");
return 1;
}
char str[100]="hello";
// Key point: Use strlen(str) instead of sizeof(str) to avoid writing null characters
ssize_t ret = write(fd,str,strlen(str));
printf("Wrote %ld bytes to the file",ret); // Output: Wrote 5 bytes to the file
close(fd); // Must close the file to release the file descriptor
return 0;
}
(2) read: Read from a File
c
ssize_t read(int fd, void *buf, size_t count);
fd: Source file descriptor;buf: Buffer to receive data;count: Maximum length of the buffer;- Return value: >0 for number of bytes actually read, ==0 for end of file, <0 for read failure.
Practical Example (05read.c): Read content from /etc/passwd
c
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
int main()
{
int fd = open("/etc/passwd", O_RDONLY); // Open system file in read-only mode
if (-1 == fd)
{
fprintf(stderr, "open error\n");
return 1;
}
char buf[50] = {0};
while (1) {
bzero(buf, sizeof(buf)); // Clear the buffer
int ret = read(fd, buf, sizeof(buf)-1); // Reserve 1 byte for '\0'
if (ret <= 0) {
break; // Exit loop when reading is complete or failed
}
printf("{%d}:{%s}\n",ret,buf); // Print read length and content
}
close(fd);
return 0;
}
2.3 Practical Case: File Copy (06readcp.c)
Implement a simplified cp command using read/write, with the core logic of "loop reading from source file → writing to target file":
c
#include <fcntl.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
int main(int argc ,char **argv)
{
// Check parameters: source and target files must be provided
if (argc < 3)
{
printf("usage:./a.out srcfile dstfile\n");
return 1;
}
// Open source file (read-only) and target file (write-only/create/truncate)
int fd_src = open(argv[1], O_RDONLY);
int fd_dst = open(argv[2], O_WRONLY|O_CREAT|O_TRUNC,0666);
if (-1 == fd_dst || -1 ==fd_src)
{
fprintf(stderr, "open error\n");
return 1;
}
// Loop read/write: 1024-byte buffer improves efficiency
while (1) {
char buffer[1024] = {0};
int ret = read(fd_src, buffer, sizeof(buffer));
if (ret <= 0)
{
break;
}
write(fd_dst, buffer, ret); // Write according to actual read length
}
// Close file descriptors
close(fd_dst);
close(fd_src);
return 0;
}
2.4 lseek: Adjust File Offset
lseek is used to move the file read/write pointer, enabling functions such as "getting file size" and "creating sparse files":
c
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
int main()
{
int fd = open("1.txt", O_RDWR);
if (-1 == fd)
{
fprintf(stderr, "open error\n");
return 1;
}
// Method 1: Get file size (offset to end, return offset value)
long size = lseek(fd, 0, SEEK_END);
printf("size %ld\n",size);
// Method 2: Create sparse file (write at 1MB offset)
lseek(fd, 1024*1024, SEEK_SET);
char str[]="travel";
write(fd,str,strlen(str));
close(fd);
return 0;
}
2.5 Standard Input/Output (07stdin.c)
In Linux, 0/1/2 correspond to standard input (stdin), standard output (stdout), and standard error (stderr) respectively, which can be directly operated via file descriptors:
c
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
char buf[10]={0};
printf("pls input num:");
fflush(stdout); // Flush buffer to ensure prompt is output first
read(0,buf,sizeof(buf)); // Read input from stdin (0)
int num =atoi(buf); // Convert to integer
write(2,&num,4); // Write integer (binary) to stderr (2)
return 0;
}
III. Practical Standard IO (C Library)
Standard IO is a wrapper of File IO by the C library, with built-in buffering and better cross-platform compatibility. Core functions include fopen/fread/fwrite/fseek/ftell.
3.1 Core Functions: File Read/Write and Offset
Standard IO uses FILE* pointers to identify files. Combined with fseek/ftell, it can easily implement "file content insertion" (02insert.c):
c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
FILE*fp = fopen("1.txt","r+"); // Open in read-write mode
if(NULL == fp)
{
fprintf(stderr,"fopen error\n");
return 1;
}
// 1. Get file size: offset to end and get offset value
fseek(fp,0,SEEK_END);
long size = ftell(fp);
printf("size is %ld\n",size);
// 2. Prepare insertion: save content after the specified position
int pos = 15; // Insertion position
char insert_str[100]="hello";
fseek(fp,pos,SEEK_SET); // Offset to insertion position
char * data =(char*)malloc(size); // Allocate memory to save the latter part
fread(data,size-pos,1,fp); // Read the latter part of the content
// 3. Insert content: overwrite with new content + original latter part
fseek(fp,pos,SEEK_SET);
fwrite(insert_str, strlen(insert_str), 1, fp); // Write inserted content
fwrite(data, size-pos, 1, fp); // Write original latter part
// 4. Release resources
fclose(fp);
free(data);
return 0;
}
3.2 Practical Case: Dictionary Query (03dict.c)
Implement a simple dictionary query function by reading files with Standard IO's fgets and storing data in a linked list:
c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "list.h"
// Dictionary data structure: word + meaning + linked list node
typedef struct
{
char word[50];
char mean[512];
struct list_head node;
} DATATYPE;
// Add word to linked list
int add_word(struct list_head* head, char* word, char* mean)
{
DATATYPE* p = (DATATYPE*)malloc(sizeof(DATATYPE));
if (NULL == p)
{
fprintf(stderr, "add malloc error\n");
return -1;
}
strcpy(p->word, word);
strcpy(p->mean, mean);
list_add(&p->node, head); // Insert into linked list
return 0;
}
// Find word
DATATYPE* find_word(struct list_head* head, char* want_word)
{
DATATYPE* p = NULL;
DATATYPE* n = NULL;
// Safely traverse the linked list
list_for_each_entry_safe(p, n, head, node)
{
if (0 == strcmp(want_word, p->word))
{
return p;
}
}
return NULL;
}
int main(int argc, char** argv)
{
// 1. Open dictionary file
FILE* fp = fopen("/home/linux/dict.txt", "r");
if (NULL == fp)
{
fprintf(stderr, "open error\n");
return 1;
}
// 2. Initialize linked list and read dictionary file
struct list_head dict_head;
INIT_LIST_HEAD(&dict_head);
while (1)
{
char str[1024] = {0};
if (NULL == fgets(str, sizeof(str), fp)) // Read line by line
{
break;
}
// Split word and meaning
char* word = strtok(str, " ");
char* mean = strtok(NULL, "\r");
add_word(&dict_head, word, mean); // Add to linked list
}
// 3. Interactive word query
while (1)
{
char want_word[50] = {0};
printf("pls input want_word:");
fgets(want_word, sizeof(want_word), stdin); // Read user input
want_word[strlen(want_word) - 1] = '\0'; // Remove newline character
if(0 == strcmp(want_word,"#quit")) // Exit condition
{
break;
}
// Find and output result
DATATYPE* tmp = find_word(&dict_head,want_word);
if(NULL == tmp)
{
printf("cant find word:%s\n",want_word);
}
else
{
printf("word:%s mean:%s\n",tmp->word,tmp->mean);
}
}
return 0;
}
IV. Core Summary
4.1 Key Conclusions
- Underlying Relationship: Standard IO is a wrapper of File IO. Choose Standard IO for cross-platform needs, and File IO for device or real-time scenarios;
- Buffering Impact: Standard IO's buffer reduces the number of system calls (improving efficiency), while File IO has no buffer (higher real-time performance);
- Identifier Difference : File IO uses file descriptors (int), and Standard IO uses FILE stream pointers (
FILE*); - Error Handling: File IO returns -1 for failure, while Standard IO returns NULL (e.g., fopen). Return values must be strictly verified.
4.2 Selection Recommendations
- For regular files (e.g., txt, configuration files): Prefer Standard IO (
fopen/fread/fwrite); - For device files (e.g., serial ports, network cards): Prefer File IO (
open/read/write); - For cross-platform requirements: Must use Standard IO;
- For precise control of file offset/permission: Use File IO.
V. Common Issues and Precautions
- File Permissions : The
modeparameter ofopen(e.g., 0666) is affected byumask(actual permission = mode & ~umask); - Buffering Issues : Standard IO requires
fflushto flush the buffer to avoid data not being written to disk; - Memory Leaks : Manually release
mallocmemory, file descriptors, and stream pointers in Standard IO; - Parameter Validation : The
countparameter ofread/writemust be set appropriately (e.g., usesizeof(buf)-1forreadto avoid buffer overflow).