Linux中的文件读写操作
(一)Linux中“一切皆文件”
1.文件
在Linux系统中,一切皆文件,文件类型根据其表示的意义,分为:
- 普通文件
- 设备文件:代表一个具体的硬件设备
- 管道文件、FIFO文件:具有特殊意义的文件,用于进程间通信;
- 套接字文件:用于网络通信;
所有这些文件都可以用一套API来操作,最基本的四个API是:
- 打开:open
- 读文件:read
- 写文件:write
- 关闭:close
2.文件标识符 fd
在使用这些API操作文件的时候,需要传入文件标识符 fd(file descriptor),文件标识符的本质就是在进程中代码某一个具体文件的整数,在使用 open 函数打开一个文件时被唯一分配,一般情况下,fd的值如果从0开始分配,如果 fd 为负数,则表示文件打开失败或者操作失败。
需要注意,fd的值在使用open打开文件时被唯一分配,在使用close关闭文件时会被回收。
举个例子,比如第一次打开文件时分配的值fd = 0,如果该文件被关闭,下次新打开一个文件时,fd依然为0,但如果之前打开的文件没有被close掉,则下次新打开一个文件时,fd递增为1,一直递增到系统设定的同时打开文件的最大值为止,这个最大值可以使用 ulimit -n 查看,一般为1024。
另外,文件描述符的值0、1、2被系统占用,在桌面Linux系统上表示:
- fd = 0:表示标准输入(stdin),对应系统的键盘
- fd = 1:表示标准输出(stdout),对应系统的显示器
- fd = 2:表示标准错误输出(stderr),对应系统的显示器
而在嵌入式Linux系统中,一般不存在显示器和键盘,都是用串口交互,所以这三个值对应的设备如下:
- fd = 0:表示标准输入(stdin),对应系统的控制台串口
- fd = 1:表示标准输出(stdout),对应系统的控制台串口
- fd = 2:表示标准错误输出(stderr),对应系统的控制台串口
(二)Linux C库提供的文件操作API
1.头文件
在使用文件操作API时,必须首先包含以下头文件:
#include <sys/types.h> //定义了一些常用数据类型,比如size_t #include <fcntl.h> //定义了open、creat等函数,以及表示文件权限的宏定义 #include <unistd.h> //定义了read、write、close、lseek等函数 #include <errno.h> //与全局变量errno相关的定义 #include <sys/ioctl.h> //定义了ioctl函数
2.open — 打开文件
操作文件之前必须要打开文件,获取文件描述符fd,该函数原型如下:
int open( const char *pathname, //文件路径+文件名称 int flags);//文件打开方式 int open( const char *pathname, //文件路径+文件名称 int flags,//文件打开方式 mode_t mode);//打开文件的权限 //返回值int:打开成功则返回文件描述符,打开失败则返回-1,同时设置全局变量errno的值来表示错误原因;
flags标志的值可以使用在<fcntl.h>
的宏定义:
O_RDONLY
:只读O_WRONLY
:只写O_RDWR
:可读可写(常用)O_CREAT
:如果要打开的文件不存在,则创建新文件O_EXCL
:如果使用O_CREAT时文件已经存在,则返回错误消息O_TRUNC
:如果文件已经存在,且成功打开,则删除文件中原来的全部数据O_APPEND
:以追加写入方式打开文件,打开之后文件指针指向文件末尾
这些打开方式可以使用|
操作符,一起使用。
mode的值表示创建新文件时设置的权限,用8进制数来表示,也可以使用<fcntl.h>中定义的宏定义来表示,但是八进制数比较方便。
3bit的八进制数分别对应linux中文件的三个权限:
rwxrwxrwx
第一个rwx是文件拥有者的权限,第二个rwx是文件拥有者所在用户组的权限,第三个rwx是其它用户组的权限;
每一个rwx都对应一个8进制数,比如0x7就表示rwx三项权限全有,0x0就表示rwx三个权限全没有,比如:
0700
:所属用户有rwx权限,当前用户组和其他用户组的用户没有任何权限;0664
(常用):所属用户有rw-权限(-表示没有此项对应权限),当前用户组的其它用户拥有rw-权限,其它用户组的用户只有r–权限。
3.read — 读取文件
函数原型如下:
ssize_t read( int fd, //文件描述符 void *buf, //用来接收所读数据的缓冲区 size_t count);//请求读取的字节数 //返回值:读取成功则返回读取的字节数,读取到文件尾则返回0,读取失败则返回-1,同时设置全局变量errno的值来表示错误原因;
4.write — 写入文件
函数原型如下:
ssize_t write( int fd, //文件描述符 const void *buf, //存放待写入数据的缓冲区 size_t count);//请求写入的字节数 //返回值:写入成功则返回实际写入的字节数,写入失败则返回-1,同时设置全局变量errno的值来表示错误原因;
5.close — 关闭文件
API原型如下:
int close( int fd);//要关闭文件的文件描述符 //如果关闭成功,返回0,否则返回-1,同时设置全局变量 errno 报告具体错误的原因。
(三)文件操作示例程序
范例实现功能:
打开当前目录下的text.txt文件,如果不存在则创建,首先写入一个字符串,然后关闭文件,再重新打开读取该文件中的内容并打印。
范例代码:
#include <sys/types.h> //定义了一些常用数据类型,比如size_t #include <fcntl.h> //定义了open、creat等函数,以及表示文件权限的宏定义 #include <unistd.h> //定义了read、write、close、lseek等函数 #include <errno.h> //与全局变量errno相关的定义 #include <sys/ioctl.h> //定义了ioctl函数 #include <stdio.h> int main(void) { int fd = -1; int res = 0; char filename[] = "test.txt"; char write_dat[] = "Hello World!"; char read_buf[128] = {0}; /* 写入文件操作示例 */ //1. 打开文件 fd = open(filename, O_RDWR | O_CREAT, 0664); if(fd < 0) { printf("%s file open fail,errno = %d.\r\n", filename, errno); return -1; } //2. 读取内容 res = write(fd, write_dat, sizeof(write_dat)); if(res < 0) { printf("write dat fail,errno = %d.\r\n", errno); return -1; } else { printf("write %d bytes:%s\r\n", res, write_dat); } //3. 关闭文件 close(fd); /* 读取文件数据示例 */ //1. 打开文件 fd = open(filename, O_RDONLY); if(fd < 0) { printf("%s file open fail,errno = %d.\r\n", filename, errno); return -1; } //2. 写入内容 res = read(fd, read_buf, sizeof(read_buf)); if(res < 0) { printf("read dat fail,errno = %d.\r\n", errno); return -1; } else { printf("read %d bytes:%s\r\n", res, read_buf); } //3. 关闭文件 close(fd); return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了