所有执行 I/O 操作的系统调用(文件IO)都以文件描述符(fd),一个非负整数(通常是小整数),来指代打开的文件。文件描述符用以表示所有类型的已打开文件,包括管道(pipe)、FIFO、socket、终端、设备和普通文件。在程序开始运行之前,shell 代表程序打开这 3 个文件描述符(0,1,2)代表标准输入输出出错。
文件IO相关主要函数:open read write lseek close
1.打开操作:open系统调用既能打开一个业已存在的文件,也能创建并打开一个新文件。
#include<sys/stat.h>
#include<fcntl.h>
int open(const char *pathname,int flags,mode_t mode)
|成功返回文件描述符,失败返回-1|
参数 flags 为位掩码,用于指定文件的访问模式,所列位掩码常量可进行逻辑或(|)操作,读写或者只读只写不能同时出现。mode 参数则指定了由 open()调用创建文件的访问权限,如果open()并未指定O_CREAT标志,则可以省略mode参数。
O_RDONLY:以只读方式打开文件
O_WRONLY:以只写方式打开文件
O_RDWR:以读写方式打开文件
O_CLOEXEC:设置 close-on-exec 标志
O_CREAT:若文件不存在则创建之
O_DIRECT:无缓冲的输入/输出
O_DIRECTORY:如果 pathname 不是目录,则失败
O_EXCL:结合 O_CREAT 参数使用,专门用于创建文件
O_LARGEFILE:在 32 位系统中使用此标志打开大文件
O_NOATIME :调用 read()时,不修改文件最近访问时间
O_NOCTTY:不要让 pathname(所指向的终端设备)成为控制终端
O_NOFOLLOW:对符号链接不予解引用
O_TRUNC:截断已有文件,使其长度为零
O_APPEND:总在文件尾部追加数据
O_ASYNC:当 I/O 操作可行时,产生信号(signal)通知进程
O_DSYNC:提供同步的 I/O 数据完整性
O_NONBLOCK:以非阻塞方式打开
O_SYNC:以同步方式写入文件
2.读取操作:read系统调用从文件描述符 fd 所指代的打开文件中读取数据
#include<unistd.h>
sszie_t read(int fd,void*buffer,size_t count);
|读取成功返回读取的字节数(读取到EOF)返回0,失败返回-1|。
count 参数指定最多能读取的字节数-包括换行符。(size_t 数据类型属于无符号整数类型。)buffer 参数提供用来存放输入数据的内存缓冲区地址。缓冲区至少应有 count 个字节。一次 read()调用所读取的字节数可以小于请求的字节数。对于普通文件而言,这有可能是因为当前读取位置靠近文件尾部。当 read()应用于其他文件类型时,比如管道、FIFO、socket 或者终端,在不同环境下也会出现 read()调用读取的字节数小于请求字节数的情况。例如,默认情况下从终端读取字符,一遇到换行符(\n),read()调用就会结束。
3.写入操作:write()系统调用将数据写入一个已打开的文件中
#include<unistd.h>
sszie_t write(int fd,void*buffer,size_t count);
|成功返回写入的字节数,失败返回-1|。
write()调用的参数含义与read()调用相类似。buffer参数为要写入文件中数据的内存地址,count参数为欲从 buffer 写入文件的数据字节数,fd 参数为一文件描述符,指代数据要写入的文件。如果 write()调用成功,将返回实际写入文件的字节数,该返回值可能小于 count 参数值。这被称为“部分写”。对磁盘文件来说,造成“部分写”的原因可能是由于磁盘已满,或是因为进程资源对文件大小的限制。对磁盘文件执行 I/O 操作时,write()调用成功并不能保证数据已经写入磁盘。因为为了减少磁盘活动量和加快 write()系统调用,内核会缓存磁盘的 I/O 操作。
4.文件偏移量:文件偏移量是指执行下一个 read()或 write()操作的文件起始位置,会以相对于文件头部起始点的文件当前位置来表示。文件第一个字节的偏移量为 0。
#include<unistd.h>
off_t lseek(int fd,off_t offset,int whence)
|成功返回新的文件偏移量,失败返回-1|。
文件打开时,会将文件偏移量设置为指向文件开始,以后每次 read()或 write()调用将自动对其进行调整,以指向已读或已写数据后的下一字节。因此,连续的 read()和 write()调用将按顺序递进,对文件进行操作。针对文件描述符 fd 参数所指代的已打开文件,lseek()系统调用依照 offset 和 whence 参数值调整该文件的偏移量。offset 参数指定了一个以字节为单位的数值。
whence 参数则表明应参照哪个基点来解释 offset 参数:
SEEK_SET:将文件偏移量设置为从文件头部起始点开始的 offset 个字节。
SEEK_CUR:相对于当前文件偏移量,将文件偏移量调整 offset 个字节1 。
SEEK_END:将文件偏移量设置为起始于文件尾部的 offset 个字节。
5.关闭操作:close()系统调用关闭一个打开的文件描述符,并将其释放回调用进程,供该进程继续使用。当一进程终止时,将自动关闭其已打开的所有文件描述符。
#include<unistd.h>
int close(int fd);
|成功返回0,失败返回-1|。
实例:
#include<stdio.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
int fd;
int err(char *err){
printf("%s\n",err);
close(fd);
exit(0);
}
int main(int argc,char *argv[]){
int num;int len;
char buff[256];
char *ss="------文本内容如下------";
printf("%s\n",ss);
system("cat 1.txt");printf("\n");
num=read(0,buff,256);buff[num]='\0';
printf("标准输入read-num=%d,read-buff:%s",num,buff);//标准输入可读取到换行符
fd=open("1.txt",O_RDWR);
if(fd== -1)err("open error");
while((num=read(fd,buff,13))>0){
buff[num]='\0';//防止最后一次读取缓冲区有其它数据-测试
printf("read-num=%d,read-buff:%s\n",num,buff);
};
//if(num==0)exit(0);//后边写操作注释掉这行避免程序退出 0a1b2c3d4e5f
if(num==-1)err("read error");
num=write(fd,"test!",strlen("test!"));//继续用同个fd则尾部追加
if(num==-1)err("write error");
printf("write-num=%d\n",num);
len=lseek(fd,0,SEEK_CUR);printf("SEEK_CUR:%d\n",len);//写操作完成--偏移量为文件大小
len=lseek(fd,5,SEEK_SET);printf("SEEK_SET:%d\n",len);//--开头5偏移量
len=lseek(fd,-5,SEEK_CUR);printf("SEEK_CUR:%d\n",len);//--开头0偏移量
len=lseek(fd,-5,SEEK_END);printf("SEEK_END:%d\n",len);//写操作前的位置
system("cat 1.txt");printf("\n");
close(fd);
return 0;
}