DAY28 Linux File IO and Standard IO Explained: From Concepts to Practice

编程达人挑战赛·第5期 10w+人浏览 386人参与

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:

FeatureFile IO (System Call)Standard IO (C Library)
Operation Interfaceopen/read/write/close/lseekfopen/fread/fwrite/fclose/fseek
Identifier TypeFile descriptor (int; e.g., 0/1/2 for stdin/stdout/stderr respectively)FILE stream pointer (FILE*)
Buffering MechanismNo buffer (direct kernel operations)With user-mode buffer (reduces system call frequency)
Cross-platform SupportOS-dependent (e.g., Linux-specific)Cross-platform (compliant with C standards)
Applicable ScenariosDevice files, real-time requirementsRegular files, general scenarios pursuing efficiency
Permission ControlSpecified 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.

#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

#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
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

#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
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

#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”:

#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”:

#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:

#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):

#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:

#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

  1. 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;
  2. Buffering Impact: Standard IO’s buffer reduces the number of system calls (improving efficiency), while File IO has no buffer (higher real-time performance);
  3. Identifier Difference: File IO uses file descriptors (int), and Standard IO uses FILE stream pointers (FILE*);
  4. 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

  1. File Permissions: The mode parameter of open (e.g., 0666) is affected by umask (actual permission = mode & ~umask);
  2. Buffering Issues: Standard IO requires fflush to flush the buffer to avoid data not being written to disk;
  3. Memory Leaks: Manually release malloc memory, file descriptors, and stream pointers in Standard IO;
  4. Parameter Validation: The count parameter of read/write must be set appropriately (e.g., use sizeof(buf)-1 for read to avoid buffer overflow).

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值