文件IO是使用系统调用(内核提供的函数)来完成数据的读写操作,不提供缓冲区,基于文件描述符操作文件,每进行一次文件io操作,进程就会从用户空间向内核空间进行一次切换,效率没有标准io高。
文件描述符
1、文件描述符本质上是一个整数,当文件存储在本地磁盘时,属于静态文件,当使用open函数打开文件时,该文件就属于动态文件,后期需要对文件进行操作的化就需要基于一个句柄来完成,这个句柄就是文件描述符,用一个整数表示。
2、文件描述符是一个非负整数,一个进程能够使用的文件描述符默认为1024个【0,1023】,可以通过指令uimit -a查看,并且可以通过ulimit -n进行修改进程能够打开的文件描述符个数
3、使用原则: 最小未分配原则
4、特殊的文件描述符:0、1、2,分别对应了标准输入、标准输出、标准出错
stdin->_fileno ===> 0 ===> STDIN_FILENO
stdout->_fileno ===> 1 ===> STDOUT_FILENO
stderr->_fileno ===> 2 ===> STDERR_FILENO
#include <myhead.h>int main(int argc, char const *argv[])
{printf("stdin -> _fileno = %d\n",stdin ->_fileno); //0printf("stdout -> _fileno = %d\n",stdout ->_fileno); //1printf("stderr -> _fileno = %d\n",stderr ->_fileno); //2FILE *fp = NULL;if ((fp = fopen("./file.txt","w")) == NULL){perror("fopen error");return -1;}printf("fp -> _fileno = %d\n", fp -> _fileno);//3 最小未分配return 0;
}
myhead.h 包含头文件如下
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);
功能:以指定的方式打开指定的文件
参数1:要打开的文件路径
参数2:文件打开标识
必须包含以下三者之一:
O_RDONLY:以只读的形式打开文件
O_WRONLY:以只写的形式打开文件
O_RDWR:以读写的形式打开文件
以下的可以选择性加入,使用位或完成:
O_CREAT:如果文件存在,就直接打开,如果文件不存在,则创建文件,此时,参数3必须要加,表示创建的文件权限
O_TRUNC:表示清空文件内容
O_APPEND:以追加的形式打开文件
O_EXCL:确保打开的是不存在的文件,通常跟O_CREAT一起使用,表示本次操作必须创建新文件,如果文件存在,则open函数报错,错误 码为:EEXIST
——————————————————————————————————————
"r":O_RDONLY
"r+":O_RDWR
"w":O_WRONLY|O_CREAT|O_TRUNC
"w+":O_RDWR|O_CREAT|O_TRUNC
"a":O_WRONLY|O_APPEND|O_CREAT
"a+":O_RDWR|O_APPEND|O_CREAT——————————————————————————————————————
参数3:当参数2中有O_CREAT属性时,参数3必须加上,表示本次创建文件的文件权 限,
但是,最终的权限不是用户给定的权限,
文件最终的权限是 给定的权限 mode&~umask的值,umask可以通过指令umask进行查看当前终端的默认掩码,
可以通过 umask -n 来将掩码更改成 n,但是,这种方式更改的umask只在当前终端 有效,其他终端无效
也可以通过umask(n)来更改umask的值
#include <sys/stat.h>mode_t umask(mode_t cmask);
功能:更改当前的umask的值
参数:要更改的umask的值
普通文件权限一般为:0664
目录文件权限一般为:0775
——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
返回值:成功返回一个新的文件描述符,失败返回-1并置位错误码
man fopen
close 关闭文件
#include <unistd.h>
int close(int fd);
功能:关闭文件描述符
参数:要关闭的文件描述符
返回值:成功返回0,失败返回-1并置位错误码
#include <unistd.h>int close(int fd);功能:关闭文件描述符参数:要关闭的文件描述符返回值:成功返回0,失败返回-1并置位错误码
#include<myhead.h>int main(int argc, const char *argv[])
{//创建一个文件描述符变量,用于存储文件描述符int fd = -1;if((fd = open("./file.txt", O_WRONLY|O_CREAT|O_TRUNC, 0664)) == -1){perror("open error");return -1;}printf("open success fd = %d\n", fd); //3//关闭文件if(close(fd) == -1){perror("close fd 1");return -1;}return 0;
}
read/write 数据读写
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
功能:向指定的文件中,写入指定的信息
参数1:已打开的文件描述符
参数2:要写入的数据的起始地址
参数3:要写入的数据大小
返回值:成功返回写入字符的个数,失败返回-1并置位错误码
#include <unistd.h>ssize_t read(int fd, void *buf, size_t count);
功能:从指定的文件中读取count个字节的数据到buf中
参数1:打开的文件描述符
参数2:要存放数据的容器起始地址
参数3:要读取的字节个数
返回值:成功返回读取字节的个数,失败返回-1并置位错误码
#include <unistd.h>ssize_t write(int fd, const void *buf, size_t count);功能:向指定的文件中,写入指定的信息参数1:已打开的文件描述符参数2:要写入的数据的起始地址参数3:要写入的数据大小返回值:成功返回写入字符的个数,失败返回-1并置位错误码#include <unistd.h>ssize_t read(int fd, void *buf, size_t count);功能:从指定的文件中读取count个字节的数据到buf中参数1:打开的文件描述符参数2:要存放数据的容器起始地址参数3:要读取的字节个数返回值:成功返回读取字节的个数,失败返回-1并置位错误码
#include <myhead.h>typedef struct
{char name[20];int age;double score;
} Stu;int main(int argc, char const *argv[])
{int fd = -1; // 创建一个文件描述符变量,用于存储文件描述符if ((fd = open("./file.txt", O_WRONLY | O_CREAT | O_TRUNC, 0664)) == -1){perror("open error");return -1;}printf("open success fd = %d\n", fd); // 3Stu s[3] = {{"张三", 10, 99.5}, {"李四", 18, 90.5}, {"王五", 16, 89.5}};write(fd, s, sizeof(s));// 关闭文件if (close(fd) == -1){perror("close fd 1");return -1;}if ((fd = open("./file.txt", O_RDONLY, 0664)) == -1){perror("open error");return -1;}printf("open success fd = %d\n", fd); // 3Stu temp;read(fd,&temp,sizeof(temp));printf("%s %d %.2lf\n",temp.name,temp.age,temp.score);return 0;
}
lseek
#include <sys/types.h>
#include <unistd.h>off_t lseek(int fd, off_t offset, int whence);
功能:移动文件的光标
参数1:文件描述符
参数2:偏移量
>0:向后偏移
=0:不偏移
<0:向前偏移
参数3:偏移起始位置
SEEK_SET:文件开头
SEEK_END:文件结尾
SEEK_CUR:文件当前位置
返回值:成功返回文件光标当前的位置,失败返回-1并置位错误码
#include<myhead.h>int main(int argc, const char *argv[])
{//打开文件,以读写的形式int fd = -1;if((fd = open("./gg.bmp", O_RDWR)) == -1){perror("open error");return -1;}//偏移光标lseek(fd, 2, SEEK_SET);//定义变量接受大小int img_size = 0;read(fd, &img_size, sizeof(img_size));printf("img_size = %d\n", img_size);//将光标直接偏移到文件末尾,返回值就是文件大小printf("size = %ld\n", lseek(fd, 0, SEEK_END));//关闭文件close(fd);return 0;
}
使用文件IO完成两个文件的拷贝
#include <myhead.h>
int main(int argc, char const *argv[])
{if (argc != 3){// printf("终端输入有误\n");write(2,"input file error\n",sizeof("input file error\n"));return -1;}umask(0000);int fd = open(argv[1],O_RDONLY);int fb = open(argv[2],O_RDWR|O_CREAT|O_TRUNC,0664);char temp[128] = "";while (1){int flag = read(fd,temp,sizeof(temp));if (flag == 0){break;}write(fb,temp,flag);}//下面子规方法会拷贝多余的信息// while (read(fd,temp,sizeof(temp)))// {// write(fb,temp,sizeof(temp));// }close(fd);close(fb);return 0;
}
文件描述符拷贝问题
拷贝用文件:
1、简单完成两个文件描述符变量的拷贝
这种情况下,没有新文件描述符产生,使用的是同一个文件描述符,共享同一个文件光标
#include <myhead.h>
int main(int argc, char const *argv[])
{int fd1 = -1;if ((fd1 = open("./file2.txt",O_RDONLY)) == -1){perror("open eroor");return -1;}//1、// int fd2 = fd1; //定义变量存储文件描述符 // //未出现新的文件描述符fd1 fd2 公用光标//2、 // int fd2 = dup(fd1); //出现新的文件描述符 // //但仍然公用光标lseek(fd2,8,SEEK_SET); //光标偏移char buf[128] = "";read(fd1,buf,5);printf("buf = %s\n",buf); //1、buf = 55555 //2、buf = 55555 close(fd1);return 0;
}
2、dup函数完成拷贝
该操作,会生成新的文件描述符,但是与旧的文件描述符共享同一个光标
#include <myhead.h>
int main(int argc, char const *argv[])
{int fd1 = -1;if ((fd1 = open("./file2.txt",O_RDONLY)) == -1){perror("open eroor");return -1;}//1、// int fd2 = fd1; //定义变量存储文件描述符 // //未出现新的文件描述符fd1 fd2 公用光标//2、 // int fd2 = dup(fd1); //出现新的文件描述符 // //但仍然公用光标lseek(fd2,8,SEEK_SET); //光标偏移char buf[128] = "";read(fd1,buf,5);printf("buf = %s\n",buf); //1、buf = 55555 //2、buf = 55555 close(fd1);return 0;
}
3、使用dup2拷贝文件描述符
int dup2(int oldfd, int newfd);
功能:拷贝旧文件描述符,放入新文件描述符中,如果新文件描述符之前已经打开,则在拷贝时,默认将其关闭
参数1:旧文件描述符
参数2:新文件描述符
返回值:成功返回新文件描述符,失败返回-1并置位错误码
#include <myhead.h>
int main(int argc, char const *argv[])
{int fd2 = open("./04.c",O_RDONLY);int fd1 = open("./file2.txt",O_RDONLY);//使fd2指向fd1指向的文件,fd2原本指向的文件静默关闭dup2(fd1,fd2); //不产生新的文件描述符//共用光标printf("fd1= %d,fd2 = %d\n",fd1,fd2); //4 3lseek(fd2,8,SEEK_SET);char buf[128] = "";read(fd1,buf,5);printf("buf = %s\n",buf); //buf = 55555close(fd1);close(fd2);return 0;
}
4、dup2的常规用法
#include <myhead.h>
int main(int argc, char const *argv[])
{int fd = -1;if ((fd = open("./aa.txt",O_WRONLY|O_CREAT|O_TRUNC))){perror("open error");return -1;}//将标准输出重定向到fd中,标准输出静默关闭dup2(fd,STDOUT_FILENO);dup2(fd,STDIN_FILENO);//将标准输入重定向到fd中dup2(fd,STDERR_FILENO);//将标准出错重定向到fd中printf("hello\n");return 0;
}
5、多次使用open函数打开同一个文件时,这多个文件描述符是不共享文件光标的
#include <myhead.h>
int main(int argc, char const *argv[])
{int fd1 = -1;if ((fd1 = open("./file2.txt",O_RDONLY))){perror("open error");return -1;}int fd2 = -1;if ((fd2 = open("./file2.txt",O_RDONLY))){perror("open error");return -1;}lseek(fd1,8,SEEK_SET); //移动fd1的光标char buf[10] = ""; //从读取fd2的指向读取数据read(fd2,,buf,5);printf("%s\n",buf); // 55555return 0;
}
文件属性函数
stat 函数原型
#include <sys/types.h>#include <sys/stat.h>#include <unistd.h>int stat(const char *pathname, struct stat *statbuf);功能:获取给定的文件状态属性参数1:要获取的文件名参数2:文件属性结构体指针,是一个地址传递,需要传递一个结构体变量的地址,通过函数调用结束后,该变量中就有内容了struct stat {dev_t st_dev; /* 设备号 */ino_t st_ino; /* Inode 号 */mode_t st_mode; /* 文件类型和文件权限 */nlink_t st_nlink; /* 硬链接数 */uid_t st_uid; /* 拥有者的用户id号 */gid_t st_gid; /* 拥有者的组id号 */dev_t st_rdev; /* 特殊文件的设备号 */off_t st_size; /* 总大小,以字节为单位 */blksize_t st_blksize; /* 块的大小 */blkcnt_t st_blocks; /* 总块数 */};如何获取文件的类型和文件权限1> 使用st_mode & S_IFMT可以得到文件的类型S_IFSOCK 0140000 socketS_IFLNK 0120000 symbolic linkS_IFREG 0100000 regular fileS_IFBLK 0060000 block deviceS_IFDIR 0040000 directoryS_IFCHR 0020000 character deviceS_IFIFO 0010000 FIFO2> 使用st_mode & 0777可以得到文件的权限S_ISUID 04000 set-user-ID bitS_ISGID 02000 set-group-ID bit (see below)S_ISVTX 01000 sticky bit (see below)S_IRWXU 00700 owner has read, write, and execute permissionS_IRUSR 00400 owner has read permissionS_IWUSR 00200 owner has write permissionS_IXUSR 00100 owner has execute permissionS_IRWXG 00070 group has read, write, and execute permissionS_IRGRP 00040 group has read permissionS_IWGRP 00020 group has write permissionS_IXGRP 00010 group has execute permissionS_IRWXO 00007 others (not in group) have read, write, andexecute permissionS_IROTH 00004 others have read permissionS_IWOTH 00002 others have write permissionS_IXOTH 00001 others have execute permission返回值:成功返回0,失败返回-1并置位错误码
#include<myhead.h>int main(int argc, const char *argv[])
{//判断外部传来的文件if(argc != 2){printf("input file error\n");return -1;}//定义接受文件的属性结构体变量struct stat sb; //stat buf//调用函数,获取文件状态if(stat(argv[1], &sb) == -1){perror("stat error");return -1;}//程序执行至此,sb中就记录了传入的文件的信息//输出文件类型switch(sb.st_mode & S_IFMT){case S_IFSOCK:{printf("套接字文件\t");}break;case S_IFLNK:{printf("链接文件\t");}break;case S_IFREG:{printf("普通文件\t");}break;case S_IFBLK:{printf("块设备文件\t");}break;case S_IFDIR:{printf("目录文件\t");}break;case S_IFCHR:{printf("字符设备文件\t");}break;case S_IFIFO:{printf("管道文件\t");}break;}//输出文件权限printf("%#o\t", sb.st_mode&0777);//输出文件大小printf("%ld\t", sb.st_size);//输出文件inode号printf("%ld\n", sb.st_ino);return 0;
}
使用文件I0完成,将源文件中的所有内容进行加密(大写转小写、小写转大写)后写入目标文件中源文件内容不变
#include <myhead.h>
int main(int argc, char const *argv[])
{if (argc != 3){// printf("终端输入有误\n");write(2, "input file error\n", sizeof("input file error\n"));return -1;}umask(0000);int fd = open(argv[1], O_RDONLY);int fb = open(argv[2], O_RDWR | O_CREAT | O_TRUNC, 0664);char temp = {0}; // 定义中转变量while (1){int flag = read(fd, &temp, 1);if (flag == 0){break;}if (temp >= 'A' && temp <= 'Z') // 大小转小写{temp = temp + 32;}else if (temp >= 'a' && temp <= 'z') // 小写转大写{temp = temp - 32;}write(fb, &temp, flag);}close(fd);close(fb);return 0;
}
结果
#INCLUDE <MYHEAD.H>
INT MAIN(INT ARGC, CHAR CONST *ARGV[])
{
IF (ARGC != 3)
{
// PRINTF("终端输入有误\N");
WRITE(2, "INPUT FILE ERROR\N", SIZEOF("INPUT FILE ERROR\N"));
RETURN -1;
}UMASK(0000);
INT FD = OPEN(ARGV[1], o_rdonly);
INT FB = OPEN(ARGV[2], o_rdwr | o_creat | o_trunc, 0664);CHAR TEMP = {0}; // 定义中转变量
WHILE (1)
{
INT FLAG = READ(FD, &TEMP, 1);
IF (FLAG == 0)
{
BREAK;
}
IF (TEMP >= 'a' && TEMP <= 'z') // 大小转小写
{
TEMP = TEMP + 32;
}
ELSE IF (TEMP >= 'A' && TEMP <= 'Z') // 小写转大写
{
TEMP = TEMP - 32;
}WRITE(FB, &TEMP, FLAG);
}CLOSE(FD);
CLOSE(FB);RETURN 0;
}