1.基础概念:
文件与内容操作
文件由内容和属性组成。内容是文件存储的数据,而属性可能包括文件的创建时间、修改时间、权限等。对文件的操作可以分为对内容的操作和对属性的操作。
文件的打开:
在访问一个文件之前,通常需要先将其打开。这是因为操作系统需要将文件加载到内存中,以便进行读写操作。如果一个文件没有被打开,它就存在于磁盘上,而不是在内存中
谁打开文件:
用户可以通过命令行(如bash)启动进程来打开文件。进程是操作系统进行资源分配和调度的基本单位。
操作系统的角色:
操作系统负责管理文件的打开和关闭。它确保进程能够安全地访问文件,并且在不需要时释放资源。操作系统通过系统调用(如open
)来打开文件,这是进程与操作系统交互的一种方式。
文件描述符:
操作系统使用文件描述符来跟踪每个打开的文件。文件描述符是一个数字,用于在程序中引用特定的文件。一定存在一种数据结构体(如进程控制块PCB)来描述被打开的文件,这有助于操作系统管理文件的打开状态。
进程与文件的关系:
进程有task_struct
,这是操作系统用来描述进程的数据结构。它包含了进程的状态信息,包括打开的文件。研究进程与文件的关系,实际上是研究进程如何通过操作系统来访问和管理文件。
2.fopen函数:
2.1 函数原型
2.2 参数解释
pathname就是路径名,就是你想打开的那个文件的路径
mode是指文件的打开模式,具体的模式可以参考下图

只读模式 ("r") :打开一个已经存在的文件,用于读取操作。如果文件不存在,返回 NULL
。
写入模式 ("w") :打开一个文件,用于写入操作。如果文件不存在,创建新文件。如果文件已存在,清空文件内容。
追加模式 ("a"):打开一个文件,用于追加写入操作。如果文件不存在,创建新文件。如果文件已存在,写入内容追加到文件末尾。
读写模式 ("r+"):打开一个已经存在的文件,用于读写操作。文件指针放在文件的开头。
写入和读取模式 ("w+"):打开一个文件,用于读写操作。如果文件不存在,创建新文件。如果文件已存在,清空文件内容。文件指针放在文件的开头。
追加和读取模式 ("a+"):打开一个文件,用于追加写入和读取操作。如果文件不存在,创建新文件。如果文件已存在,写入内容追加到文件末尾。文件指针放在文件的末尾。
二进制只读模式 ("rb"):类似于只读模式,但用于二进制文件。
二进制写入模式 ("wb"):类似于写入模式,但用于二进制文件。
二进制追加模式 ("ab"):类似于追加模式,但用于二进制文件。
二进制读写模式 ("rb+"):类似于读写模式,但用于二进制文件。
二进制写入和读取模式 ("wb+"):类似于写入和读取模式,但用于二进制文件。
二进制追加和读取模式 ("ab+"):类似于追加和读取模式,但用于二进制文件。
2.3 返回值
成功时,fopen
返回一个指向 FILE
结构的指针,该结构用于后续的文件操作。如果打开文件失败,返回 NULL
,并设置 errno
以指示错误原因。
2.4 错误处理
如果文件打开失败,可以通过检查返回值是否为 NULL
来确定。可以使用 perror
或 str
error 函数来获取错误描述。
2.5 代码示例
#include <stdio.h>
int main() {
FILE *fp;
fp = fopen("example.txt", "r"); // 以只读模式打开文件
if (fp == NULL) {
perror("Error opening file");
return -1;
}
// 进行文件操作...
fclose(fp); // 关闭文件
return 0;
}
2.6 注意事项
1、文件指针 :fopen
返回的 FILE
指针用于后续的文件读写操作。
2、文件路径 :fopen
需要文件的完整路径或相对于当前工作目录的路径。当前工作目录(CWD)可以通过 getcwd
函数获取。
3、文件模式:不同的模式允许不同的文件操作,如读取、写入、追加等。
4、使用 fopen
打开文件后,应该在完成文件操作后调用 fclose
来关闭文件,以释放资源。
5、在处理文件时,始终检查 fopen
的返回值,以确保文件正确打开。
3.echo

当我们输入echo "hello word" > log.txt的时候,会重定向到log.txt文件中,必须先打开这个文件,之后才能写入,默认是w方式,所以我们下次写的时候如果用>,原文件内容就会被清空,而如果使用>>就不会
4.输出信息到显示器的方法
1.通过键盘输入数字(如12345),讨论输入的是int 12345还是一个1 2 3 4 5,答案是后者,说明显示器被叫做字符设备
2.我今天向显示器写入12345,我是写了一个int 12345 ,还是向显示器写入了int 12345?答案是向显示器写入了字符1 2 3 4 5 ,所以说显示器是字符设备!!
5.标准输入输出流
标准输入输出流:
stdin:标准输入流,通常与键盘文件关联。
stdout:标准输出流,通常与显示器文件关联。
stderr:标准错误流,通常与显示器文件关联,用于输出错误信息。
而我们的进程在启动的时候,会默认打开三个输入输出流,本质上就是打开三个文件,换句话说,凡是使用CPU计算的,都要进程默认打开对应的文件
那我们不妨来查看一下这三个标准流:
我们输入man 3 stdin

6.fopen
首先我们查看一下这个函数

6.1 参数解释
-
pathname
:- 文件路径和文件名。用于指定要打开或创建的文件。
2.flags:
打开文件的方式,由一组标志位组合而成。常用的标志包括:
O_RDONLY
:以只读方式打开。
O_WRONLY
:以只写方式打开。
O_RDWR
:以读写方式打开
O_APPEND
:写入时追加到文件末尾。
O_CREAT
:如果文件不存在,则创建它,并使用mode
参数指定的权限。
O_TRUNC
:如果文件存在,则截断文件长度为0。
这些标志可以使用按位或运算符(|
)组合,例如:
open("file.txt", O_RDWR | O_CREAT, 0644);
实际上,这些每个标志都是一个宏,定义为一个数字,其中只有一个比特位是1,其他位是0。这样可以通过按位或运算符组合多个标志。
3.mode
:
文件权限模式,用于指定新创建文件的权限。权限模式是一个mode_t
类型的值,通常由以下三位权限组成:即前文所讲的rwx,也可以用数字表示,如666是所有权限均为可读可写,rw-
注意:
1.如果文件已经存在,可以只使用两个参数(pathname
和flags
)来打开文件
6.2 位图传递标志位
6.2.1 定义
位图传递标志位是一种通过位运算组合多个标志位的方法。在文件操作中,标志位用于指定文件的打开模式(如只读、只写、读写、追加等)。每个标志位是一个单独的位,通过按位或运算符(|
)将它们组合在一起。
6.2.2 常见的标志位总结
标志位 | 描述 |
---|---|
O_RDONLY |
以只读方式打开文件 |
O_WRONLY |
以只写方式打开文件 |
O_RDWR |
以读写方式打开文件 |
O_APPEND |
写入时追加到文件末尾 |
O_CREAT |
如果文件不存在,则创建文件 |
O_TRUNC |
如果文件存在,则截断文件长度为0 |
O_EXCL |
与O_CREAT 一起使用,如果文件已存在则打开失败 |
6.2.3 位图传递标志位的原理
每个标志位对应一个独立的位,通常定义为宏,表示为一个数字,其中只有一个比特位为1。例如
#define O_RDONLY 00
#define O_WRONLY 01
#define O_RDWR 02
#define O_APPEND 04
#define O_CREAT 0100
#define O_TRUNC 01000
#define O_EXCL 02000
这些宏定义允许通过按位或运算符组合多个标志位。例如:
int flags = O_RDWR | O_APPEND | O_CREAT;
上述代码组合了读写模式、追加模式和创建模式。
6.2.4 优点
灵活性:允许组合多个标志位,满足不同的文件操作需求。
可读性:通过宏定义,使代码更具可读性,易于理解和维护。
跨平台性 :位图传递标志位在不同操作系统上可能具有不同的定义,但其使用方式类似。跨平台编程中,标准库函数(如fopen
)会封装这些差异,提供统一的接口。
7.文件描述符
7.1 定义
在操作系统中,文件描述符(File Descriptor,FD)是一个用于访问文件的整数,它是一个数组索引,指向内核中的文件描述符表。文件描述符表维护了文件的状态和操作信息。
7.2 文件描述符的管理
内核管理:每个进程都维护着一个文件描述符表,包含多个文件描述符。内核通过这些描述符来管理文件的打开、读取、写入和关闭操作。
标准文件描述符:通常,进程启动时会默认打开三个文件描述符:
0:标准输入(stdin
)
1
:标准输出(stdout
)
2
:标准错误(stderr
)
7.3 跨平台性
跨平台性是指软件能够在多种不同的操作系统上运行,无需修改或只需少量修改。在文件操作中,不同操作系统(如Linux、Windows、macOS)可能有不同的文件接口和实现方式。
7.3.1为什么需要跨平台性?
1.提高语言竞争力:支持跨平台可以让编程语言吸引更广泛的用户群体,提升其使用率和影响力。
2.便捷性:开发者无需针对不同平台编写不同代码,降低了开发成本和复杂度。
7.3.2 如何实现跨平台性?
标准库封装 :高级语言通常会提供标准库封装底层的系统调用,如C语言的<stdio.h>
库。这些库在不同平台上实现细节可能不同,但对开发者提供了统一的接口。
Linux :调用open
、read
等系统调用。
Windows :调用CreateFile
、ReadFile
等API。
条件编译 :开发者使用条件编译指令(如#ifdef
)来编写可在不同平台运行的代码。
#ifdef LINUX
// Linux系统调用
#elif defined(WIN32)
// Windows API调用
#endif