这篇文章在 文章:【文件I/O】文件持久化 的基础之上,更进一步的描述了文件IO中更多更详细的API详解。
文件IO
文件IO操作是必须要理解的环节之一,因为 s
:套接字文件 ;p
: 管道文件 —> 都需要通过文件IO来进行打开。
文件IO的特点
1>文件IO没有缓冲区
2>文件IO源于底层,有系统的地方就能使用文件IO
3>文件IO操作比较复古
4>文件IO的操作核心:文件描述符(通过文件描述符来访问文件)
文件I/O
不带缓冲
不带缓冲指的是每个read和write都调用内核中的相应系统调用 --->开销很大
不带缓冲的I/O函数不是ANSI C的组成部分,但是是POSIX和XPG3的组成部分--->文件IO来自系统底层
通过文件描述符来访问文件-----**
对于内核而言,所有打开文件都由文件描述符引用。
文件描述符是一个非负整数。当打开一个现存文件或创建一个新文件时,内核向进程返回一个文件描述符。
当读、写一个文件时,用open或creat返回的文件描述符标识该文件,将其作为参数传送给read或write。
在posix.1应用程序中,幻数0、1、2应被代换成符号常数STDIN_FILENO、STDOUT_FILENO、STDERR_FILENO。
这些常数都定义在头文件<unistd.h>中。
文件IO的API学习:
文件IO操作的层面 和 系统底层非常有关系 ***
1> 打开文件
open
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
功能:
打开 文件 ,并返回 该文件的文件描述符
参数:
pathname:带路径文件名
flags:打开的方式
mode:*****--->隐藏开启参数
返回值:
成功返回 该文件的文件描述符
失败返回 -1,并设置错误码
open的重点;
flags:打开的方式
三个必选项:互斥-->三选一
O_RDONLY 只读
O_WRONLY 只写
O_RDWR 读写
其他都是可选项:
O_CREAT: 创建-->如果文件不存在,则创建
O_TRUNC: 清空模式
O_APPEND: 追加模式
O_NOCTTY: 使用本参数时,如文件为终端,那么终端不可以作为调用open()系统调用的那个进程的控制终端。
mode:*****--->隐藏开启参数
如果你的flags参数中具有O_CREAT-->那么就会开启mode参数(第三个参数)
mode:设置你创建文件的权限
标准IO的打开方式 VS 文件IO的打开方式
r O_RDONLY
r+ O_RDWR
w O_WRONLY|O_CREAT|O_TRUNC,0666
w+ O_RDWR|O_CREAT|O_TRUNC,0666
a O_WRONLY|O_CREAT|O_APPEND,0666
a+ O_RDWR|O_CREAT|O_APPEND,0666
open 和 fopen 的区别:
1> 头文件差别很大:
open:三个头文件:sys/types.h、sys/stat.h、fcnt1.h --->系统头文件:来自系统库
fopen:一个头文件:stdio.h
2> fopen 和 open 使用的场景有什么区别?
fopen: 标准IO的打开: 作用仅用来负责IO操作
open: 文件IO的打开: 作用很广泛:
1> 打开系统的特殊文件:p文件 和 s文件 b文件 c文件
open打开p文件时,你可以使用p文件本身的特点: 管道通信
简单点说:
标准IO的fopen仅用来IO操作
文件IO的open可以用来操作底层
touch创建还是open创建,都要收到掩码 ---> 默认掩码:0002
创建文件:默认权限:0666
创建目录:默认权限:0777
但是,它们权限会受到掩码的影响
实际权限:默认权限 - 掩码权限: 0666 - 0002 =0664
默认权限 - 掩码权限: 0777 - 0002 =0775
2> 操作文件-------文件IO的基本操作流程:1. 打开文件;2. 读写文件;3. 关闭文件;
read-->读取
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
功能:
从相应的文件描述符中,读取数据
参数:
1>fd:文件描述符
2>buf:读取出来的数据存放的地方 char buf[100];
3>count:读取的字符数大小
返回值:
成功返回 读取到的字符数
读到末尾返回0
失败返回 -1,并设置错误码
write-->写入
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
功能:
将数据 写入到 相应的文件描述符
参数:
1>fd:文件描述符
2>buf:写入的数据存放的地方 char buf[100];
3>count:写入的字符数大小
返回值:
成功返回 写入到的字符数
失败返回 -1,并设置错误码
课堂练习:
将 hello.c的内容(自定义) 读取后 写入到 nihao.c 中 --->一模一样只有文件名不同
3> 关闭文件 --->验证文件描述符的处理逻辑
close
#include <unistd.h>
int close(int fd);
功能:
关闭文件描述符
参数:
fd:文件描述符
返回值:
成功返回 0
失败返回 -1,并设置错误码
4> 适用函数
4.1> lseek(); //文件IO的光标控制 --->和fseek相比,少了个小弟
#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);
功能:
文件IO的光标控制
参数:
fd:文件描述符
offset:偏移量
whence:起始位置/基准位置
SEEK_SET:文件开头
SEEK_CUR:当前位置
SEEK_END:文件尾部
返回值:
成功返回 光标当前位置的偏移量
失败返回 -1,并设置错误码
4.2> dup/dup2
#include <unistd.h>
int dup(int oldfd); //会自动创建newfd
功能;
根据 oldfd 返回一个 newfd
作用:绑定 文件描述符 : oldfd 返回 newfd -->newfd会继承 oldfd的东西
操作newfd 等于 操作oldfd
参数:
oldfd:必须是已开启的文件返回的文件描述符
返回值:
成功返回 newfd
失败返回 -1,并设置错误码
#include <unistd.h>
int dup2(int oldfd, int newfd);
功能;
将oldfd 和 newfd 进行绑定
情况: oldfd必须已存在,newfd无所谓
newfd:不存在则创建并且和oldfd绑定
newfd:已存在,会使原本newfd代表的文件失效,然后绑定
参数:
oldfd:必须是已开启的文件返回的文件描述符
返回值:
成功返回 newfd
失败返回 -1,并设置错误码
标准IO的格式化IO:---> 可以掌握,将数据结构的数据链,保存在文件中---->自己悟
int fprintf(FILE *stream, const char *format, ...);//将流的内容输出到屏幕上中
int dprintf(int fd, const char *format, ...);//将文件描述符的内容输出到屏幕上中
int sprintf(char *str, const char *format, ...);//将某地址内容输出到屏幕上 -->char buf[100];
#include <stdio.h>
int scanf(const char *format, ...);
int fscanf(FILE *stream, const char *format, ...);//输入内容到流中
int sscanf(const char *str, const char *format, ...);//输入内容到某地址上
文件指针操作
文件指针用于标识文件中当前的读写位置,操作文件指针可以实现文件的随机访问。
常用函数:
fseek(FILE *stream, long offset, int origin)
:- 移动文件指针到指定位置。
- origin 参数:
- SEEK_SET:文件开头。
- SEEK_CUR:当前位置。
- SEEK_END:文件末尾。
ftell(FILE *stream)
:- 返回当前文件指针的位置。
rewind(FILE *stream)
:- 将文件指针重新定位到文件开头。
附代码:
#include <stdio.h>
int main() {
FILE *file = fopen("example.txt", "r");
if (file == NULL) {
perror("Error opening file");
return 1;
}
fseek(file, 5, SEEK_SET); // 文件指针移动到第 5 个字节
int pos = ftell(file); // 获取文件指针位置
printf("Current position: %d\n", pos);
rewind(file); // 重置文件指针到开头
fclose(file);
return 0;
}
文件 I/O对于二进制文件操作
二进制文件操作允许程序以更高效的方式处理数据(如图像、音频等)。
读取二进制文件:
#include <stdio.h>
int main() {
FILE *file = fopen("data.bin", "rb"); // 二进制读取
if (file == NULL) {
perror("Error opening file");
return 1;
}
int buffer[10];
fread(buffer, sizeof(int), 10, file); // 读取 10 个整数
fclose(file);
for (int i = 0; i < 10; i++) {
printf("%d ", buffer[i]);
}
return 0;
}
写入二进制文件:
#include <stdio.h>
int main() {
FILE *file = fopen("data.bin", "wb"); // 二进制写入
if (file == NULL) {
perror("Error opening file");
return 1;
}
int data[] = {1, 2, 3, 4, 5};
fwrite(data, sizeof(int), 5, file); // 写入数组
fclose(file);
return 0;
}
注意事项:
- 文件关闭:始终在完成操作后关闭文件,避免文件资源泄漏。
- 缓冲区刷新:写入文件时,数据可能被暂存于缓冲区,调用
fflush()
或fclose()
确保数据写入磁盘。 - 权限检查:确保操作系统文件权限允许对文件进行读写操作。
- 错误处理:文件操作可能失败(如文件不存在、磁盘空间不足等),应检查返回值并处理错误。
文件 I/O 是程序与外部存储设备交互的核心功能,涵盖了文件的打开、读取、写入、关闭等操作。使用文件 I/O可以实现高效的数据存储与处理。
综上。希望该内容能对你有帮助,感谢!
以上。仅供学习与分享交流,请勿用于商业用途!转载需提前说明。
我是一个十分热爱技术的程序员,希望这篇文章能够对您有帮助,也希望认识更多热爱程序开发的小伙伴。
感谢!