目录
一、思维导图
二、标准IO函数(时间相关的函数)
2.1 time
2.2 localtime
练习:利用time和localtime循环显示时间
三、文件IO函数
3.1 文件描述符
3.1.1 概念
3.1.2 特殊的文件描述符
3.1.3 文件描述符的总量
3.2 文件IO的函数
1) open
练:创建一个权限为777的文件
2) umask
练:用umask函数 创建一个权限为777的文件
3) close
4) write
练:将字符串写入文件中
5) read
练1:用read和write函数实现 拷贝文件到终端(两种方法)
练2:用read和write函数实现 拷贝图片
6) lseek
练1:用lseek函数 计算文件大小
练2:用read函数 计算文件大小
3.3 获取文件属性
1)stat
2)提取文件的权限
练:文件权限提取 并打印
三、作业
3.1 【read】计算文件大小
3.2 【使用循环】文件权限提取并打印
功能:从1970-1-1日至今的秒数;
原型:#include
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);
功能:将1970-1-1日至今的秒数转换成日历格式;
原型:
#include
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
#include
#include
int main(int argc, const char *argv[])
{
time_t t=0;
struct tm* info=NULL;
while(1)
{
t = time(NULL);
info = localtime(&t);
printf("%d-%02d-%0d %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;
}
注意点:
- 文件IO是不带缓冲区的
- 文件IO函数是由操作系统提供的,与操作系统绑定,又称之为系统调用。
- 标准IO是通过流指针维护一个文件,文件IO是通过文件描述符来维护一个文件。
- 尝试打开一个文件的时候,系统会自动给这个文件分配一个编号,用这个编号来描述这个文件。这个编号就是文件描述符。
- 文件描述符的本质:数组下标。在main函数启动前,操作系统会帮助我们在用户空间创建一个数组(文件描述符表),且容量默认为1024. 范围是[0, 1023]
- 文件描述符申请原则:从小到大依次遍历,直到遇到没有被使用的文件描述符
特殊的流指针 |
特殊的文件描述符 |
|
FILE* stdin |
stdin->_fileno |
0 |
FILE* stdout |
stdout->_fileno |
1 |
FILE* stderr |
stderr->_fileno |
2 |
默认是1024个
- 循环打开不关闭,直到超出打开上限,得到文件描述符总量。
- getdtablesize()函数:获取操作系统的文件描述符总量。 printf("%d\n", getdtablesize());
open / close
read / write
lseek
功能:打开一个文件;
原型:
#include
#include #include 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进行组合。
┌─────────────┬───────────────────────────────┐
│fopen() mode │ open() flags │
├─────────────┼───────────────────────────────┤
│ r │ O_RDONLY │
├─────────────┼───────────────────────────────┤
│ w │ O_WRONLY | O_CREAT | O_TRUNC │
├─────────────┼───────────────────────────────┤
│ a │ O_WRONLY | O_CREAT | O_APPEND │
├─────────────┼───────────────────────────────┤
│ r+ │ O_RDWR │
├─────────────┼───────────────────────────────┤
│ w+ │ O_RDWR | O_CREAT | O_TRUNC │
├─────────────┼───────────────────────────────┤
│ a+ │ O_RDWR | O_CREAT | O_APPEND │
└─────────────┴───────────────────────────────┘
输入0777
结果为0775
#include
#include
#include
#include
#include
int main(int argc, const char *argv[])
{
int fd = open("./open.txt", O_WRONLY|O_CREAT|O_TRUNC,0777);//出现775
if(fd <0)
{
ERR_MSG("open");
return -1;
}
printf("open success\n");
return 0;
}
i)umask是什么?
文件权限掩码,目的就是影响文件创建时候的权限。可以通过设置umask的值保证某些用户肯定没有某些权限。
ii)获取umask的值
终端输入:umask
iii)修改umask的值
- 终端输入: umask 0 只在设置终端有效
- umask()函数
#include
#include mode_t umask(mode_t mask); umask(0);
输入0777
结果为0777
#include
#include
#include
#include
#include
int main(int argc, const char *argv[])
{
//将本程序的umake的值改为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");
if(close(fd) < 0)
{
ERR_MSG("close");
return -1;
}
printf("关闭成功\n");
return 0;
}
功能:关闭文件; 释放文件描述符;
原型:
#include
int close(int fd); 参数: int fd:指定要关闭的文件描述符;
返回值:
成功,返回0;
失败,返回-1,更新errno;
//关闭文件
if(close(fd) < 0)
{
ERR_MSG("close");
return -1;
}
功能:将数据写入到文件中;
原型:
#include
ssize_t write(int fd, const void *buf, size_t count); 参数:
int fd:指定要将数据写入到哪个文件中,填对应的文件描述符;
void *buf:指定要输出的数据的首地址,可以是任意类型数据;
size_t count:指定要输出的数据字节数;
返回值:
成功,返回成功输出的字节数;
失败,返回-1,更新errno;
注意:write函数指定写多少个字节,就会从内存中拿多少个字节,写入到文件中,即使越界
#include
#include
int main(int argc, const char *argv[])
{
//将本程序的umake的值改为0
umask(0);
//以w的形式打开
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[20]= "hello world";
res = write(fd, buf, sizeof(buf));
printf("res = %ld\n", res);
if(close(fd) < 0)
{
ERR_MSG("close");
return -1;
}
printf("关闭成功\n");
return 0;
}
功能:从文件中读取数据;
原型:
#include
ssize_t read(int fd, void *buf, size_t count); 参数:
int fd:指定要从哪个文件中读取数据,填对应的文件描述符;
void *buf:指定要将读取到的数据存储到那块空间中,可以是任意类型数据;
size_t count:指定要读取的数据字节数;
返回值:
>0, 成功,返回成功读取的字节数;
=0, 文件读取完毕;
=-1, 失败,返回-1,更新errno;
#include
#include
int main(int argc, const char *argv[])
{
//将本程序的umake的值改为0
umask(0);
//以a的形式打开
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));
res = read(fd, buf, sizeof(buf)-1);
if(0 == res)
break;
printf("%s", buf);
}
printf("拷贝完毕\n");
#endif
//关闭文件
if(close(fd) < 0)
{
ERR_MSG("close");
return -1;
}
printf("关闭成功\n");
return 0;
}
用read和write函数实现,拷贝图片。
提示ls -l 查看图片类型
eog 图片 ---- >打开图片
思路:
- ls-l查看图片类型
- 打开两个文件
- 读一次写一次拷贝
- 关闭文件
- eog +图片名.png 【打开图片】
#include
#include
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;
}
printf("open success\n");
size_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;
}
printf("fd_r关闭成功\n");
//关闭目标文件
if(close(fd_w)<0)
{
ERR_MSG("close");
return -1;
}
printf("fd_w关闭成功\n");
return 0;
}
功能:修改文件偏移量;
原型:
#include
#include 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);
#include
#include
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);
//关闭
if(close(fd)<0)
{
ERR_MSG("close");
return -1;
}
printf("关闭成功\n");
return 0;
}
#include
#include
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 buf[128] = "";
ssize_t count=0, res=0;
//将偏移量移到文件开头
lseek(fd, 0,SEEK_SET);
//读一次记一次
while(1)
{
bzero(buf, sizeof(buf));
res = read(fd, buf, sizeof(buf));
if(0 == res)
break;
count+=res;
}
printf("count=%ld\n",count);
//关闭
if(close(fd)<0)
{
ERR_MSG("close");
return -1;
}
printf("关闭成功\n");
return 0;
}
功能:获取文件的属性;
原型:
#include
#include #include 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]
mode: 0100664 ---> rw-rw-r-- 664----> 110 110 100 ---> 0664 100 000 000 ---> 0400 ------------ 100 000 000 ===> 0400结果不等于0,需要打印'r'。否则打印'-' 664----> 110 110 100 ---> 0664 010 000 000 ---> 0200 ------------ 010 000 000 ===> 0200结果不等于0,需要打印'w'。否则打印'-' 664----> 110 110 100 ---> 0664 001 000 000 ---> 0100 ------------ 000 000 000 ===> 0000结果等于0,需要打印'-'。否则打印'x'
#include
#include
void get_filePermission(mode_t m)
{
if(m & 0400)
putchar('r');
else
putchar('-');
if(m & 0200)
putchar('w');
else
putchar('-');
if(m & 0100)
putchar('x');
else
putchar('-');
///
if(m & 0040)
putchar('r');
else
putchar('-');
if(m & 0020)
putchar('w');
else
putchar('-');
if(m & 0010)
putchar('x');
else
putchar('-');
/
if(m & 0004)
putchar('r');
else
putchar('-');
if(m & 0002)
putchar('w');
else
putchar('-');
if(m & 0001)
putchar('x');
else
putchar('-');
}
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);
putchar(10);
//文件的硬链接数
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;
}
#include
#include
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 buf[128] = "";
ssize_t count=0, res=0;
//将偏移量移到文件开头
lseek(fd, 0,SEEK_SET);
//读一次记一次
while(1)
{
bzero(buf, sizeof(buf));
res = read(fd, buf, sizeof(buf));
if(0 == res)
break;
count+=res;
}
printf("count=%ld\n",count);
//关闭
if(close(fd)<0)
{
ERR_MSG("close");
return -1;
}
printf("关闭成功\n");
return 0;
}
方法一:
void get_filePermission(mode_t m)
{
char buf[]="rwx";
for(int i=0;i<9;i++)
{
if( (m & (0400)>>i) == 0)
{
putchar('-');
continue;
}
printf("%c",buf[i%3]);
}
putchar(10);
}
方法二:
void get_filePermission(mode_t m)
{
char buf[]="rwx";
for(int i=0;i<9;i++)
{
if( (m & (0400)>>i) == 0)
{
putchar('-');
continue;
}
switch(i%3)
{
case 0:putchar('r');break;
case 1:putchar('w');break;
case 2:putchar('x');break;
}
}
putchar(10);
}
方法三:
思路:
- 需要打印的权限共分为3组,可通过进制右移取得一组的结果
- 循环三次即可得出3组的结果
- 详细参考2.9的get_filePermission函数
#include
#include
void get_filePermission(mode_t m)
{
int i=0;
mode_t mask=0400;
for(i=0;i<3;i++) //共三组
{
if(m & mask)
putchar('r');
else
putchar('-');
mask >>= 1; //右移一位
if(m & mask)
putchar('w');
else
putchar('-');
mask >>= 1; //右移一位
if(m & mask)
putchar('x');
else
putchar('-');
mask >>= 1; //右移一位
}
}
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);
putchar(10);
//文件的硬链接数
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;
}