目录
5、多次使用open函数打开同一个文件时,这多个文件描述符是不共享文件光标的
使用文件I0完成,将源文件中的所有内容进行加密(大写转小写、小写转大写)后写入目标文件中源文件内容不变
文件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); //0
printf("stdout -> _fileno = %d\n",stdout ->_fileno); //1
printf("stderr -> _fileno = %d\n",stderr ->_fileno); //2
FILE *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); // 3
Stu 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); // 3
Stu 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并置位错误码
通过 lseek 对 .bmp 格式的文件进行数据读取
#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 3
lseek(fd2,8,SEEK_SET);
char buf[128] = "";
read(fd1,buf,5);
printf("buf = %s\n",buf); //buf = 55555
close(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");//不在终端输出,而是输入到fd指向的文件中
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); // 55555
return 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 socket
S_IFLNK 0120000 symbolic link
S_IFREG 0100000 regular file
S_IFBLK 0060000 block device
S_IFDIR 0040000 directory
S_IFCHR 0020000 character device
S_IFIFO 0010000 FIFO
2> 使用st_mode & 0777可以得到文件的权限
S_ISUID 04000 set-user-ID bit
S_ISGID 02000 set-group-ID bit (see below)
S_ISVTX 01000 sticky bit (see below)
S_IRWXU 00700 owner has read, write, and execute permission
S_IRUSR 00400 owner has read permission
S_IWUSR 00200 owner has write permission
S_IXUSR 00100 owner has execute permission
S_IRWXG 00070 group has read, write, and execute permission
S_IRGRP 00040 group has read permission
S_IWGRP 00020 group has write permission
S_IXGRP 00010 group has execute permission
S_IRWXO 00007 others (not in group) have read, write, and
execute permission
S_IROTH 00004 others have read permission
S_IWOTH 00002 others have write permission
S_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;
}
对文件夹的操作
opendir 打开文件夹
#include <sys/types.h>
#include <dirent.h>DIR *opendir(const char *name);
功能:打开指定的文件夹
参数:要打开的文件夹路径
返回值:成功返回文件夹指针,失败返回NULL
#include <myhead.h>
int main(int argc, char const *argv[])
{
//定义文件夹指针
DIR * pd = opendir("./");
if (NULL == pd)
{
perror("opendir");
return -1;
}
//读取文件夹
struct dirent* rp = NULL; //读取文件目录信息
while ((rp = readdir(pd)) != NULL)
{
printf("%-20s\t%#o\t%d\t%ld\n",rp->d_name,rp->d_type,rp->d_reclen,rp->d_ino);
}//文件名 文件类型 结构体大小 inode号
//关闭文件夹
closedir(pd);
return 0;
}
closedir 关闭文件夹
#include <sys/types.h>
#include <dirent.h>
int closedir(DIR *dirp);
功能:关闭指定的文件夹
参数:之前打开的文件夹指针
返回值:成功返回0,失败返回-1并置位错误码
readdir 读取文件夹信息
#include <dirent.h>
struct dirent *readdir(DIR *dirp);
功能:读取打开的文件夹中的记录信息
参数:文件夹指针
返回值:成功返回当前文件夹指针对应的目录信息,失败返回NULL并置位错误码
struct dirent {
ino_t d_ino; /* Inode 号 */
off_t d_off; /* 文件夹指针的偏移量*/
unsigned short d_reclen; /* 当前结构体的长度 */
unsigned char d_type; /* 文件的类型,不支持所有的文件类型 */
char d_name[256]; /* 文件名称 */
};
关于文件类型:
DT_BLK This is a block device.DT_CHR This is a character device.
DT_DIR This is a directory.
DT_FIFO This is a named pipe (FIFO).
DT_LNK This is a symbolic link.
DT_REG This is a regular file.
DT_SOCK This is a UNIX domain socket.
DT_UNKNOWN The file type could not be determined.