一、Xmind整理:
文件描述符概念:
二、课上练习:
练习1:用fread和fwrite实现文件拷贝
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <head.h>
int main(int argc, const char *argv[])
{//以读的方式打开源文件FILE* fp_r=fopen("./01_fopen.c","r");if(NULL==fp_r){ERR_MSG("fopen");return -1;}//以写的方式打开源文件FILE* fp_w=fopen("./copy.c","w");if(NULL==fp_w){ERR_MSG("fopen");return -1;}//读一次写一次,直到文件读取完毕char buf[128]="";size_t res=0;while(1){bzero(buf,sizeof(buf));//以128为单位,读取1个数据,所当不足128的时候//剩下的数据读取不出来,所以这个代码是错误的res=fread(buf,1,sizeof(buf),fp_r);printf("res=%ld\n",res);if(0==res)break;fwrite(buf,1,res,fp_w);}printf("拷贝完毕\n");//关闭fclose(fp_w);fclose(fp_r);return 0;
}
练习2:time
功能:从1970-1-1日至今的秒数
原型:
#include <time.h>time_t time(time_t *tloc);
参数:
time_t *tloc:若不为空,则1970-1-1日至今的秒数同样会被存储到该指针指向的内存空间中;
返回值:
成功,返回1970-1-1日至今的秒数;
失败,返回((time_t) -1),更新errno;
time_t t = time(NULL);
printf("%ld\n", t);
time_t pt;
time(&pt);
printf("%ld\n", pt);
练习3:localtime
功能:将1970-1-1日至今的秒数转换成日历格式
原型:
#include <time.h>
struct tm *localtime(const time_t *timep);
参数:
time_t *timep: 指定要转换成日历格式的秒数的首地址;
返回值:
成功,返回结构体指针; vi -t tm可以查看struct tm 成员 或者man手册往下翻
失败,返回NULL;更新errno;
struct tm {int tm_sec; /* Seconds (0-60) */ 秒int tm_min; /* Minutes (0-59) */ 分int tm_hour; /* Hours (0-23) */ 时int tm_mday; /* Day of the month (1-31) */ 日int tm_mon; /* Month (0-11) */ 月= tm_mon+1int tm_year; /* Year - 1900 */ 年= tm_year+1900int tm_wday; /* Day of the week (0-6, Sunday = 0) */ 星期int tm_yday; /* Day in the year (0-365, 1 Jan = 0) */ 一年中的第几天};
例题:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <head.h>
int main(int argc, const char *argv[])
{/*time_t t=time(NULL);printf("%ld\n",t);time _t pt;time(&pt);printf("%d\n",pt);*/time_t t;struct tm* info=NULL;while(1){t=time(NULL);info=localtime(&t);printf("%d-%02d-%02d %02d:%02d:%02d\r",\info->tm_year+1900,info->tm_mon+1,info->tm_mday,\info->tm_hour,info->tm_min,info->tm_sec);fflush(stdout);sleep(1);}return 0;
}
练习4:文件描述符的总量
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <head.h>
int main(int argc, const char *argv[])
{printf("%d %d %d\n",stdin->_fileno,stdout->_fileno,stderr->_fileno);FILE*fp=NULL;while(1){fp=fopen("./1.txt","w");if(NULL==fp){ERR_MSG("fopen");return -1;}printf("%d ",fp->_fileno);fflush(stdout);}return 0;
}
练习5: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);
参数:
char *pathname:指定要打开的文件的路径和名字;int flags:打开方式O_RDONLY 只读, O_WRONLY 只写,O_RDWR 读写----以上三种必须包含一种----------O_APPEND 追加方式打开,O_TRUNC 清空,若文件存在,且是一个普通文件,且以写的方式打开O_CREAT 若文件不存在,则创建一个普通文件。若有多个选项合成一个打开方式,则用按位或连接:"w":O_WRONLY | O_CREAT | O_TRUNCmode_t mode:用来指定文件创建的时候的权限,例如:0664若flags中包含了O_CREAT或者O_TMPFILE的时候,就必须写mode参数;若flags没有O_CREAT或者O_TMPFILE的时候,会忽略mode参数;
返回值:
成功,返回文件描述符,
失败,返回-1,更新errno;
注意:
标准IO中的 r r+ w w+ a a+,用文件IO中的flags进行组合。
练习6:umask
the mode of the created file is (mode & ~umask).
文件创建时候的真实权限是 mode & ~umask
mode: 0777 -> 111 111 111 umask?111 111 101=> ~umask -> umask = 000 000 010 = 0002
结果: 0775 ----> 111 111 101
i. umask是什么
文件权限掩码,目的就是影响文件创建时候的权限。可以通过设置umask的值保证某些用户肯定没有某些权限。
ii. 获取umask的值
终端输入:umask
iii. 修改umask的值
1.终端输入: umask 0 只在设置终端有效
2.umask()函数
#include <sys/types.h>#include <sys/stat.h>mode_t umask(mode_t mask);umask(0);
练习7:close
功能:关闭文件; 释放文件描述符
原型:
#include <unistd.h>
int close(int fd);
参数:
int fd:指定要关闭的文件描述符;
返回值:
成功,返回0;
失败,返回-1,更新errno;
练习8:write
功能:将数据写入到文件中
原型:
#include <unistd.h>ssize_t write(int fd, const void *buf, size_t count);
参数:
int fd:指定要将数据写入到哪个文件中,填对应的文件描述符;
void *buf:指定要输出的数据的首地址,可以是任意类型数据;
size_t count:指定要输出的数据字节数;
返回值:
成功,返回成功输出的字节数;
失败,返回-1,更新errno;
例题: 创建一个权限是0777的文件,并在数据写入到文件中
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <head.h>
int main(int argc, const char *argv[])
{//将本程序的umask的值修改成0umask(0);int fd=open("./open.txt",O_WRONLY|O_CREAT|O_TRUNC,0777);if(fd<0){ERR_MSG("open");return -1;}printf("open success\n");ssize_t res=0;char buf[]="hello world";res=write(fd,buf,sizeof(buf));printf("res=%ld\n",res);if(close(fd)<0){ERR_MSG("close");return -1;}printf("close success\n");return 0;
}
注意:
write函数指定写多少个字节,就会从内存中拿多少个字节,写入到文件中,即使越界
练习9:read
功能:从文件中读取数据
原型:
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
参数:
int fd:指定要从哪个文件中读取数据,填对应的文件描述符;
void *buf:指定要将读取到的数据存储到那块空间中,可以是任意类型数据;
size_t count:指定要读取的数据字节数;
返回值:
>0, 成功,返回成功读取的字节数;=0, 文件读取完毕;=-1, 失败,返回-1,更新errno;
例题1: read的使用
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <head.h>
int main(int argc, const char *argv[])
{//将本程序的umask的值修改成0umask(0);int fd=open("./01_fileno.c",O_RDONLY);if(fd<0){ERR_MSG("open");return -1;}printf("open success\n");ssize_t res=0;char buf[128]="";while(1){bzero(buf,sizeof(buf));res=read(fd,buf,sizeof(buf));if(0==res){break;}//将数据写到终端,写的个数为实际读取到的字节数write(1,buf,res);}#if 0while(1){bzero(buf,sizeof(buf));/***************************************///由于后续打印的是使用%s打印,所以需要保留一个\0位置//防止%s打印的时候越界少\0位 res=read(fd,buf,sizeof(buf)-1);if(0==res){break;}printf("%s",buf); //%s打印直到遇到\0位置}
#endifprintf("读取完毕\n");if(close(fd)<0){ERR_MSG("close");return -1;}printf("close success\n");return 0;
}
例题2:用read和write函数实现,拷贝图片
提示:1.ls-l查看图片类型
2.eog 图片--->打开图片
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <head.h>
int main(int argc, const char *argv[])
{//以读的方式打开源文件int fd_r=open("./1.png",O_RDONLY);if(fd_r<0){ERR_MSG("open");return -1;}printf("open success\n");//以写的方式打开目标文件int fd_w=open("./2.png",O_WRONLY|O_CREAT|O_TRUNC,0664);if(fd_w<0){ERR_MSG("open");return -1;}ssize_t res=0;char buf[128]="";//读一次写一次while(1){bzero(buf,sizeof(buf));res=read(fd_r,buf,sizeof(buf));if(0==res){break;}//读多少个就写多少个if(write(fd_w,buf,res)<0){ERR_MSG("write");return -1;}}printf("拷贝完成\n");//关闭if(close(fd_r)<0){ERR_MSG("close");return -1;}if(close(fd_w)<0){ERR_MSG("close");return -1;}printf("close success\n");return 0;
}
练习10:lseek
功能:修改文件偏移量
原型:
#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);
参数:
int fd:文件描述符;
off_t offset: 距离whence参数指定的偏移量。往前偏移填负数, 往后偏移填正数
int whence:SEEK_SET, 文件开头位置SEEK_CUR, 文件当前位置SEEK_END 文件结尾位置
返回值:
成功,修改偏移量后,文件当前位置距离文件开头的偏移量;
失败,返回-1,更新errno;
//计算文件大小
off_t size = lseek(fd_r, 0, SEEK_END);
printf("size=%ld\n", size);
例题:lseek的使用
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <head.h>
int main(int argc, const char *argv[])
{//以读的方式打开源文件int fd_r=open("./1.png",O_RDONLY);if(fd_r<0){ERR_MSG("open");return -1;}//计算文件大小off_t size=lseek(fd_r,0,SEEK_END);printf("size=%ld\n",size);//关闭close(fd_r);return 0;
}
注意:
若偏移量在文件开头,能否继续往前偏移 ---> 不行
若偏移量在文件结尾,能否继续往后偏移 ---> 可以
练习11:获取文件属性
练习12:stat
功能:获取文件的属性
原型:
#include <sys/types.h>#include <sys/stat.h>#include <unistd.h>int stat(const char *pathname, struct stat *statbuf);
参数:
char *pathname:指定要获取属性的文件路径以及名字;
struct stat *statbuf:存储获取到的属性;
返回值:
成功,返回0;
失败,返回-1,更新errno;vi -t stat 或者 man手册往下翻
struct stat
{ino_t st_ino; /* Inode number */ inode号mode_t st_mode; /* File type and mode */ 文件类型和权限nlink_t st_nlink; /* Number of hard links */ 硬链接数uid_t st_uid; /* User ID of owner */ 用户的uidgid_t st_gid; /* Group ID of owner */ 组用户的gidoff_t st_size; /* Total size, in bytes */ 文件大小struct timespec st_atim; /* Time of last access */ 最后一次被访问的时间struct timespec st_mtim; /* Time of last modification */ 最后一次被修改的时间struct timespec st_ctim; /* Time of last status change */ 最后一次改变状态的时间#define st_atime st_atim.tv_sec /* Backward compatibility */#define st_mtime st_mtim.tv_sec#define st_ctime st_ctim.tv_sec
};
提取文件的权限:
mode_t st_mode 本质上是一个unsigned int类型,里面存储了文件的类型和权限。
st_mode中其中低9bits存储了文件的权限:[0bit - 8bit]
例题:文件权限提取
#include <stdio.h>
#include <head.h>void get_filePermission(mode_t m) //mode_t m = buf.st_mode
{if((m & 0400) != 0)putchar('r');elseputchar('-');if((m & 0200) != 0) putchar('w');elseputchar('-');if((m & 0100) != 0)putchar('x');elseputchar('-');///if((m & 0040) != 0)putchar('r');elseputchar('-');if((m & 0020) != 0)putchar('w');elseputchar('-');if((m & 0010) != 0)putchar('x');elseputchar('-');if((m & 0004) != 0)putchar('r');elseputchar('-');if((m & 0002) != 0)putchar('w');elseputchar('-');if((m & 0001) != 0)putchar('x');elseputchar('-');return;
}
int main(int argc, const char *argv[])
{struct stat buf;if(stat("./01_fileno.c", &buf) < 0){ERR_MSG("stat");return -1;}//文件的类型和权限printf("mode: 0%o\n", buf.st_mode);get_filePermission(buf.st_mode);//文件的硬链接数printf("link: %ld\n", buf.st_nlink);//文件的所属用户printf("uid: %d\n", buf.st_uid);//文件所属组用户printf("gid: %d\n", buf.st_gid);//文件大小printf("size: %ld\n", buf.st_size);//文件的修改时间printf("time: %ld\n", buf.st_ctime);//文件的名字return 0;
}
三、课后作业:
1.用read函数计算文件的大小
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <head.h>
int main(int argc, const char *argv[])
{int fd = open("./1.png",O_RDONLY);if(fd < 0){ERR_MSG("open");return -1;}printf("open success\n");off_t size = lseek(fd,0,SEEK_END);printf("size=%ld\n",size);char c;ssize_t res = 0;int count = 0;lseek(fd,0,SEEK_SET);while(1){res = read(fd,&c,1);if(0 == res)break;count++;}printf("count=%d\n",count);if(close(fd) < 0){ERR_MSG("close");return -1;}printf("close success\n");return 0;
}
2.将课上的文件权限提取修改成循环方式
#include <stdio.h>
#include <head.h>void get_filePermission(mode_t m)
{long x=0400;char c[]="rwx";int count=0;while(x){if((m & x) != 0)putchar('r');elseputchar('-');x=x>>1;if((m & x) != 0) putchar('w');elseputchar('-');x=x>>1;if((m & x) != 0)putchar('x');elseputchar('-');x=x>>1;}return;
}
int main(int argc, const char *argv[])
{struct stat buf;if(stat("./01_fileno.c", &buf) < 0){ERR_MSG("stat");return -1;}//文件的类型和权限printf("mode: 0%o\n", buf.st_mode);get_filePermission(buf.st_mode);//文件的硬链接数printf("link: %ld\n", buf.st_nlink);//文件的所属用户printf("uid: %d\n", buf.st_uid);//文件所属组用户printf("gid: %d\n", buf.st_gid);//文件大小printf("size: %ld\n", buf.st_size);//文件的修改时间printf("time: %ld\n", buf.st_ctime);return 0;
}