文件操作:文件IO操作流程及各类函数应用+标准IO与文件IO区别
文件IO是Linux内核专门为应用层提供的文件操作方法。
一、操作流程
- 打开文件---open
- 读写文件---read/write
- 关闭文件---close
二、open函数
(1)定义:
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
(2)参数说明
功能:打开文件并获得一个文件描述符
参数
- pathname➡要打开的文件的文件名
- flags➡打开方式
打开方式的种类
O_RDONLY
:只读方式打开文件O_WRONLY
:只写方式打开文件O_RDWR
:可读可写方式打开文件O_CREAT
:文件不存在时创建文件O_TRUNC
:打开文件时清空文件内容O_APPEND
:写文件时从末尾追加内容
- mode➡文件操作权限
遵循 Linux 文件权限规则(如 rwxrwxrwx
对应八进制 0777
,rw-rw-r--
对应 0664
),用于创建新文件时设定初始权限 。
3.返回值
成功:返回文件描述符
失败:-1
<文件权限计算的操作(创建文件时)>
创建文件指定的 mode
会与系统 umask
按位与(&
),最终确定实际权限 。
示例:mode
设为 0777
,umask
为 002
(即 111 111 101
),则最终权限:
0777 & 0775 = 0775
(八进制计算,对应 rwxrwxr-x
),普通文件通常设 0664
即可满足需求
(3)文件打开 flags 与标准 I/O 模式对应
标准 I/O 模式 | 文件 I/O(open 函数 flags 组合 ) | ||
---|---|---|---|
"r" | O_RDONLY | ||
"r+" | O_RDWR | ||
"w" | `O_WRONLY | O_CREAT | O_TRUNC`(创建并清空文件 ) |
"w+" | `O_WRONLY | O_APPEND | O_CREAT`(创建并追加写 ) |
"a" | `O_RDWR | O_APPEND | O_CREAT`(追加写,可读可写 ) |
(4)文件描述符 file descriptor重要概念
- 本质:小的、非负整型数值。
- 范围:0 - 1023(共 1024 个,系统限制),是操作系统中已打开文件的标识符 。
- 分配原则:从最小未被使用的数值分配,默认打开的三个标准文件(标准输入、输出、错误)对应 fd 为 0、1、2 。
- 与标准IO的关联
标准 I/O 类型 | FILE* 类型 |
系统默认打开的标准文件及对应 fd | - 标准输入(stdin)→ fd = 0 - 标准输出(stdout)→ fd = 1 - 标准错误(stderr)→ fd = 2 |
新建文件的 fd 分配 | 从 3 开始分配 |
打开文件后未及时 close
,会因 fd 有限导致文件描述符泄露,开发需养成操作完文件立即关闭的习惯 。
(5)示例程序
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>int main(int argc , const char *argv[])
{int fd = open("1.txt",O_RDWR | O_APPEND | O_CREAT, 0664);if(-1 == fd){printf("open error\n");}printf("fd = %d\n",fd);close(fd);return 0;
}
三、close函数
(1)函数:int close(int fd);
(2)功能:关闭文件
四、read函数
(1)头文件:
#include <unistd.h>
(2)函数:
ssize_t read(int fd, void *buf, size_t count);
(3)参数说明
功能:从文件(通过 fd 标识 )中读取数据到缓冲区
buf
。参数:
fd
:文件描述符(open 获得 )。buf
:存储读取数据的内存缓冲区地址 。count
:期望读取的字节数 。
返回值:
成功:返回实际读取的字节数(可能小于
count
,如读到文件末尾 )。失败:返回
-1
;读到文件末尾返回0
。
(4)示例程序
char buff[1024] = {0};
ssize_t cnt = read(fd, buff, sizeof(buff));
if (cnt > 0)
{
printf("buff: %s\n", buff);
}
五、write函数
(1)头文件:
#include <unistd.h>
(2)函数原型:
ssize_t write(int fd, const void *buf, size_t count);
(3)参数说明
- 功能:将
buf
中数据写入文件(通过 fd 标识 )。 参数:
fd
:文件描述符 。buf
:要写入数据的内存地址 。count
:要写入的字节数(通常用strlen
计算字符串长度,或固定字节数 )。
返回值:
成功:返回实际写入的字节数 。
失败:返回
-1
。‘
(4)程序示例
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<string.h>int main(int argc , const char *argv[])
{int fd = open("2.txt",O_WRONLY | O_CREAT | O_TRUNC , 0664);if(-1 == fd){printf("open error\n");return -1;}char str[32] = {"hello"};write(fd,str,strlen(str));char *pstr="hello world";write(fd,pstr,strlen(pstr));write(fd,"how are you",11);close(fd);return 0;
}
读写函数应用示例
1.实现cat功能
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<string.h>int main(int argc, const char *argv[])
{if(argc != 2){printf("usage : ./a.out <filename>\n");return -1;}int fd = open(argv[1],O_RDONLY);if(fd < 0){printf("open error\n");return -1;}char buff[1024] = {0};while(1){ssize_t cnt = read(fd,buff, sizeof(buff));if(cnt == 0){return 0;}write(1,buff,cnt);}close(fd);return 0;
}
2.实现拷贝功能
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<string.h>int main(int argc, const char *argv[])
{if(argc != 3){printf("usage : ./a.out <srcfile> <dstfile>\n");return -1;}int srcfd = open(argv[1],O_RDONLY);int dstfd = open(argv[2],O_RDWR | O_APPEND | O_CREAT,0664);if(-1 == srcfd || -1 == dstfd){printf("open error\n");return -1;}while(1){char buff[1024] = {0};ssize_t cnt = read(srcfd , buff,sizeof(buff));if(cnt == 0){break;}else if(-1 == cnt){printf("read error\n");break;}write(dstfd,buff,cnt);}close(srcfd);close(dstfd);return 0;}
六、文件定位函数---lseek
(1)头文件:
#include <sys/types.h>
+ #include <unistd.h>
(2)函数原型:
off_t lseek(int fd, off_t offset, int whence);
(3)参数说明
- 功能:调整文件读写位置(类似 “光标移动” ),支持随机读写文件 。
- 参数:
fd
:文件描述符 。offset
:偏移量(正数向后移,负数向前移,需结合whence
理解 )。whence
:偏移的参考位置:SEEK_SET
:从文件开头算偏移 。SEEK_CUR
:从当前读写位置算偏移 。SEEK_END:
从文件末尾算偏移 。
- 返回值:
- 成功:返回当前读写位置相对于文件开头的偏移量 。
- 失败:返回
-1
。
4.实用场景:
- 求文件大小:
lseek(fd, 0, SEEK_END);
返回值即为文件总字节数(需配合变量接收返回值)。 - 重置读写位置:
lseek(fd, 0, SEEK_SET);
,让后续读写回到文件开头 。
七、标准IO和文件IO的区别
对比维度 | 标准 I/O(如 printf、fopen 等 ) | 文件 I/O(如 open、read、write 等 ) |
---|---|---|
归属与移植性 | 属于 C 库函数,移植性强。 | 属于 Linux 系统调用,依赖内核,移植性弱(仅限 Linux 类系统 )。 |
缓冲区 | 标准IO是系统调用的一次封装,增加了缓冲区,目的是提高数据读写效率。 | 无缓冲区,每次 read/write 直接触发系统调用 。 |
适用场景 | 主要用于对普通文件的操作。 | 主要应用在对硬件的操作,也可操作普通文件。 |
八、缓冲区类型(标准 I/O )
标准 I/O 因缓冲区存在,数据并非立即刷新。
(1)行缓冲(1K 缓冲区,约 1024 字节 )
- 应用场景:终端交互(如
printf
输出到控制台 )。 - 刷新时机:
- 程序正常结束(隐式触发 )。
- 遇到换行符(
\n
)。 - 调用
fflush(FILE*)
强制刷新 。 - 缓冲区写满时自动刷新 。
(2)全缓冲(4K 缓冲区,4096 字节 )
- 应用场景:普通文件读写(如
fopen
打开的文件 )。 - 刷新时机:
- 程序结束自动刷新 。
- 调用
fflush(FILE*)
强制刷新 。 - 文件关闭(
fclose
)时刷新 。 - 缓冲区写满时自动刷新 。
(3)无缓冲(0K 缓冲区 )
- 典型对象:标准错误(
stderr
),保证错误信息立即输出(如fprintf(stderr, ...)
)。
补充程序:实现单词的查找
#include <stdio.h>#include <string.h>int main(int argc, const char *argv[]){FILE *fp = fopen("./dict.txt", "r");if (NULL == fp){printf("fopen error\n");return -1;}char *pword = NULL;char *pmean = NULL;char buff[1024] = {0};char word[512] = {0};while (1){fgets(word, sizeof(word), stdin);//hello\nword[strlen(word) -1] = '\0';if (0 == strcmp(word, ".quit")){break;}while (1){char *p = fgets(buff, sizeof(buff), fp);if (NULL == p){printf("Not found\n");break;}pword = strtok(buff, " ");if (pword != NULL){pmean = strtok(NULL, "\r");if (0 == strcmp(word, pword)){printf("%s : %s\n", pword, pmean);break;}}}rewind(fp);}fclose(fp);return 0;}