【Linux探索学习】第二十弹——基础IO:深入理解C语言文件I/O与Linux操作系统中的文件操作

Linux学习笔记:

https://blog.csdn.net/2301_80220607/category_12805278.html?spm=1001.2014.3001.5482

前言:

文件I/O(输入输出)操作是现代计算机系统中的重要组成部分,几乎所有的程序都需要与文件进行交互。无论是读取配置文件、写入日志文件,还是处理用户数据,文件操作都是不可避免的。而在操作系统层面,文件操作的实现往往是通过文件描述符和系统调用接口来完成的。在Linux操作系统中,文件描述符是处理文件的关键,而通过系统调用接口,程序能够直接与操作系统交互,实现文件的打开、读取、写入和关闭等操作。

C语言作为一种底层编程语言,提供了多种文件操作函数,同时,Linux操作系统提供了底层的系统调用,帮助程序实现更精细的文件控制。在这篇博客中,我们将深入探讨C语言文件I/O操作的基础,重点讨论操作系统中的文件操作机制,详细讲解Linux中的文件描述符和文件操作系统调用接口。通过这篇文章,你将能够理解文件操作的基本概念、系统调用的工作原理以及如何在C语言中实现高效的文件操作。

目录

第一部分:C语言文件I/O操作基础

[1.1 打开文件:fopen()](#1.1 打开文件:fopen())

[1.2 读取文件:fread()](#1.2 读取文件:fread())

[1.3 写入文件:fwrite()](#1.3 写入文件:fwrite())

[1.4 关闭文件:fclose()](#1.4 关闭文件:fclose())

[1.5 错误处理](#1.5 错误处理)

第二部分:Linux操作系统中的文件操作

[2.1 文件描述符(File Descriptor)](#2.1 文件描述符(File Descriptor))

[2.2 系统调用接口](#2.2 系统调用接口)

[2.2.1 open() 系统调用](#2.2.1 open() 系统调用)

[2.2.2 read() 系统调用](#2.2.2 read() 系统调用)

[2.2.3 write() 系统调用](#2.2.3 write() 系统调用)

[2.2.4 close() 系统调用](#2.2.4 close() 系统调用)

[第三部分:文件描述符与FILE *的区别](#第三部分:文件描述符与FILE *的区别)

[3.1 FILE *与文件描述符的区别](#3.1 FILE *与文件描述符的区别)

结论


首先我们先来讲解一下C语言中的文件I/O操作,这个在我们前面C语言的讲解中已经提到过,今天我们结合操作系统的IO操作再来回顾一下

第一部分:C语言文件I/O操作基础

在C语言中,文件I/O操作主要是通过标准库提供的FILE *指针和一系列文件操作函数来实现的。这些函数为开发人员提供了更高层的文件操作接口,使得文件读写变得简单和方便。

1.1 打开文件:fopen()

C语言通过fopen()函数打开文件,并返回一个FILE *类型的指针,用于后续的文件读写操作。fopen()的语法如下:

cpp 复制代码
FILE *fopen(const char *filename, const char *mode);
  • filename:表示文件的路径,文件可以是相对路径或绝对路径。
  • mode:文件打开的模式,决定文件的读写方式。

常见的mode有:

模式 描述
"r" 以只读模式打开文件,文件必须存在。
"w" 以写模式打开文件,若文件已存在则会被清空。
"a" 以追加模式打开文件,若文件不存在则会创建文件。
"rb" 以二进制模式只读打开文件,文件必须存在。
"wb" 以二进制模式写入文件,若文件已存在则会被清空。

示例:

cpp 复制代码
#include <stdio.h>

int main() {
    FILE *file = fopen("example.txt", "w");
    if (file == NULL) {
        perror("Error opening file");
        return -1;
    }

    fprintf(file, "Hello, World!\n");
    fclose(file);
    return 0;
}

运行结果:

1.2 读取文件:fread()

fread()函数从文件中读取数据,并将其存储到内存中。其语法如下:

cpp 复制代码
size_t fread(void *ptr, size_t size, size_t count, FILE *stream);
  • ptr:指向内存的指针,用于存储读取的数据。
  • size:每个数据元素的大小(单位:字节)。
  • count:读取的元素个数。
  • stream:文件指针,指定从哪个文件读取数据。

示例:

cpp 复制代码
#include <stdio.h>

int main() {
    FILE *file = fopen("example.txt", "r");
    if (file == NULL) {
        perror("Error opening file");
        return -1;
    }

    char buffer[100];
    size_t bytesRead = fread(buffer, sizeof(char), 100, file);
    buffer[bytesRead] = '\0';  // Add null terminator
    printf("File content: %s\n", buffer);
    
    fclose(file);
    return 0;
}

运行结果:

1.3 写入文件:fwrite()

fwrite()函数用于将数据从内存写入到文件中。其语法如下:

cpp 复制代码
size_t fwrite(const void *ptr, size_t size, size_t count, FILE *stream);
  • ptr:指向内存中数据的指针。
  • size:每个数据元素的大小(单位:字节)。
  • count:写入的元素个数。
  • stream:文件指针,指定将数据写入哪个文件。

示例:

cpp 复制代码
#include <stdio.h>

int main() {
    FILE *file = fopen("example.txt", "w");
    if (file == NULL) {
        perror("Error opening file");
        return -1;
    }

    const char *message = "Hello from C!\n";
    fwrite(message, sizeof(char), 16, file);
    
    fclose(file);
    return 0;
}

运行结果:

1.4 关闭文件:fclose()

文件操作完成后,应该通过fclose()函数关闭文件。其语法如下:

cpp 复制代码
int fclose(FILE *stream);
  • stream:文件指针,指向已经打开的文件。

关闭文件后,程序无法再通过该文件指针进行任何操作。

示例:

cpp 复制代码
#include <stdio.h>

int main() {
    FILE *file = fopen("example.txt", "r");
    if (file == NULL) {
        perror("Error opening file");
        return -1;
    }

    // Perform file operations...

    fclose(file);
    return 0;
}

1.5 错误处理

在文件操作中,错误处理非常重要。C语言提供了两个函数来帮助开发者检测错误:ferror()feof()

  • ferror(FILE *stream):判断是否发生了文件I/O错误。
  • feof(FILE *stream):判断文件是否到达了末尾。

示例:

cpp 复制代码
#include <stdio.h>

int main() {
    FILE *file = fopen("example.txt", "r");
    if (file == NULL) {
        perror("Error opening file");
        return -1;
    }

    char buffer[100];
    if (fread(buffer, sizeof(char), 100, file) == 0) {
        if (ferror(file)) {
            perror("Error reading file");
        } else if (feof(file)) {
            printf("End of file reached.\n");
        }
    }

    fclose(file);
    return 0;
}

第二部分:Linux操作系统中的文件操作

在Linux中,文件操作是通过系统调用接口和文件描述符来完成的。文件描述符是一个整数,它表示一个打开的文件,而系统调用接口提供了底层文件操作的功能,如打开、读写和关闭文件。理解Linux中的文件描述符和系统调用接口对于深入了解文件I/O非常重要。

2.1 文件描述符(File Descriptor)

在Linux中,每个打开的文件都会被分配一个文件描述符。文件描述符是一个非负整数,操作系统内核通过它来跟踪进程与文件之间的关联。文件描述符提供了与文件进行交互的通道,可以通过它执行各种文件操作。

文件描述符 描述
0 标准输入(stdin)
1 标准输出(stdout)
2 标准错误(stderr)

除了标准输入、输出和错误,程序还可以使用open()系统调用打开文件并获得其他文件描述符。

2.2 系统调用接口

Linux提供了多个系统调用来执行文件操作,常见的系统调用包括:open()read()write()close()等。通过这些系统调用,程序能够直接与内核进行交互,完成文件的操作。

2.2.1 open() 系统调用

open()系统调用用于打开一个文件并返回文件描述符。其语法如下:

cpp 复制代码
int open(const char *pathname, int flags, mode_t mode);
  • pathname:要打开的文件的路径。
  • flags:文件打开模式,决定文件的访问方式。
  • mode:文件权限,通常在文件创建时使用。

常见的flags参数包括:

标志 描述
O_RDONLY 以只读模式打开文件
O_WRONLY 以只写模式打开文件
O_RDWR 以读写模式打开文件
O_CREAT 文件不存在时创建文件
O_TRUNC 如果文件已存在,清空文件内容
O_APPEND 以追加模式打开文件

示例:

cpp 复制代码
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>

int main() {
    int fd = open("example.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
    if (fd == -1) {
        perror("Error opening file");
        return -1;
    }

    const char *data = "Hello from Linux File Descriptor!";
    write(fd, data, 31);  // 写入数据到文件
    close(fd);  // 关闭文件描述符
    return 0;
}

运行结果:

2.2.2 read() 系统调用

read()系统调用用于从文件中读取数据,并将数据存储到内存中。其语法如下:

cpp 复制代码
ssize_t read(int fd, void *buf, size_t count);
  • fd:文件描述符。
  • buf:指向内存的指针,用于存储读取的数据。
  • count:要读取的字节数。

示例:

cpp 复制代码
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>

int main() {
    int fd = open("example.txt", O_RDONLY);
    if (fd == -1) {
        perror("Error opening file");
        return -1;
    }

    char buffer[100];
    ssize_t bytesRead = read(fd, buffer, sizeof(buffer) - 1);
    if (bytesRead == -1) {
        perror("Error reading file");
        close(fd);
        return -1;
    }

    buffer[bytesRead] = '\0';  // Null-terminate the string
    printf("File content: %s\n", buffer);

    close(fd);
    return 0;
}

运行结果:

2.2.3 write() 系统调用

write()系统调用用于将数据写入文件。其语法如下:

cpp 复制代码
ssize_t write(int fd, const void *buf, size_t count);
  • fd:文件描述符。
  • buf:指向要写入数据的内存地址。
  • count:要写入的字节数。

示例:

cpp 复制代码
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>

int main() {
    int fd = open("example.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
    if (fd == -1) {
        perror("Error opening file");
        return -1;
    }

    const char *message = "Hello from Linux File Descriptor!\n";
    write(fd, message, 31);  // Write data to file
    close(fd);  // Close file descriptor
    return 0;
}

运行结果:

2.2.4 close() 系统调用

close()系统调用用于关闭文件描述符。其语法如下:

cpp 复制代码
int close(int fd);
  • fd:要关闭的文件描述符。

示例:

cpp 复制代码
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>

int main() {
    int fd = open("example.txt", O_RDONLY);
    if (fd == -1) {
        perror("Error opening file");
        return -1;
    }

    // Perform file operations...

    close(fd);  // Close the file descriptor
    return 0;
}

第三部分:文件描述符与FILE *的区别

虽然C语言提供了FILE *类型和相关的标准库函数来处理文件操作,但底层实际上是通过文件描述符来进行管理的。理解FILE *与文件描述符的区别对于深入理解文件I/O非常重要。

3.1 FILE *与文件描述符的区别

特性 FILE *(C标准库) 文件描述符(Linux操作系统)
类型 由C标准库提供的类型 操作系统内核使用整数值标识
管理者 由C标准库管理 由操作系统内核管理
主要用途 提供更高级别的文件操作接口 提供更低级别的文件操作接口
缓冲区管理 提供缓冲区管理,提高效率 不提供缓冲区管理
数据访问方式 适用于文本文件的高级操作 适用于二进制文件和直接内存映射操作

结论

通过本文的详细讲解,您应该已经对C语言的文件I/O操作以及Linux操作系统中文件描述符和系统调用有了更深刻的理解。在C语言中,文件I/O操作主要通过FILE *指针和标准库函数来实现,而在Linux操作系统中,文件操作通过文件描述符和底层的系统调用接口进行。通过这些系统调用,程序能够直接与操作系统交互,完成文件的打开、读写和关闭等操作。

理解FILE *与文件描述符的区别、系统调用的工作原理,以及如何高效地进行文件操作,将有助于你在编程过程中处理更复杂的文件任务,并提高程序的性能。

本篇笔记:


感谢各位大佬支持,创作不易,还请各位大佬点赞支持!!!

相关推荐
qq_243050792 小时前
dnsmap:DNS枚举 详细完整教程 Kali Linux&Termux入门教程 黑客渗透测试 信息收集
linux·网络·安全·web安全·网络安全·系统安全·ddos
码字哥3 小时前
EasyExcel设置表头上面的那种大标题(前端传递来的大标题)
java·服务器·前端
m0_748257184 小时前
海康威视摄像头RTSP使用nginx推流到服务器直播教程
运维·服务器·nginx
Pou光明5 小时前
1_linux系统网络性能如何优化——几种开源网络协议栈比较
linux·运维·网络·网络协议·开源
fen_fen5 小时前
Docker如何运行一个python脚本Hello World
运维·docker·容器
TianyaOAO6 小时前
inmp+discuz论坛
linux·运维·服务器
雨中奔跑的小孩6 小时前
爬虫学习案例3
爬虫·python·学习
星光璀璨山河无恙6 小时前
【Linux】grep命令
大数据·linux
寒月6586 小时前
黑盒白盒测试
运维·服务器
I love this bad girl6 小时前
防火墙旁挂部署+故障切换
服务器·网络·数据库