一、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+1
int tm_year; /* Year - 1900 */ 年= tm_year+1900
int 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_TRUNC
mode_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的值修改成0
umask(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的值修改成0
umask(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 0
while(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位置
}
#endif
printf("读取完毕\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 */ 用户的uid
gid_t st_gid; /* Group ID of owner */ 组用户的gid
off_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');
else
putchar('-');
if((m & 0200) != 0)
putchar('w');
else
putchar('-');
if((m & 0100) != 0)
putchar('x');
else
putchar('-');
///
if((m & 0040) != 0)
putchar('r');
else
putchar('-');
if((m & 0020) != 0)
putchar('w');
else
putchar('-');
if((m & 0010) != 0)
putchar('x');
else
putchar('-');
if((m & 0004) != 0)
putchar('r');
else
putchar('-');
if((m & 0002) != 0)
putchar('w');
else
putchar('-');
if((m & 0001) != 0)
putchar('x');
else
putchar('-');
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');
else
putchar('-');
x=x>>1;
if((m & x) != 0)
putchar('w');
else
putchar('-');
x=x>>1;
if((m & x) != 0)
putchar('x');
else
putchar('-');
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;
}