作业:
1>
#include <myhead.h>
int main(int argc, const char *argv[])
{
FILE *fp;
if((fp = fopen("./time.txt","a")) == NULL){ //以追加形式打开文件
perror("fopen error");
return -1;
}
time_t start_time = time(NULL); //定义开始时间
char buf[13];
while(1){
time_t current_time = time(NULL); //定义现在的时间
if(current_time - start_time >= 1){ //比较现在的时间和开始时间,大于等于1则执行输入系统语句
time_t systime = time(NULL);
struct tm *tmPtr = localtime(&systime);
//获取行号
int count = 1;
char s[32];
FILE *fp1;
if((fp1 = fopen("./time.txt","r")) == NULL){ //以只读形式打开文件
perror("fopen error");
return -1;
}
while(fgets(s,sizeof(s),fp1) != NULL){
count++;
}
//向文件中输入系统时间
fprintf(fp,"%d、%02d:%02d:%02d\n",count,tmPtr->tm_hour,tmPtr->tm_min,tmPtr->tm_sec);
//刷新缓冲区
fflush(fp);
fclose(fp1);
start_time = current_time; //更新开始时间
}
}
fclose(fp);
return 0;
}
效果图:
2> 使用fread、fwrite完成两个文件的拷贝
#include <myhead.h>
int main(int argc, const char *argv[])
{
FILE *srcfp;
if((srcfp = fopen("./time.txt","r")) == NULL){
perror("srcfopen error");
return -1;
}
FILE *dstfp;
if((dstfp = fopen("./fcp.txt","w")) == NULL){
perror("dstfopen error");
return -1;
}
while(!feof(srcfp)){ //读取到文件结束时循环结束
char src[16];
fread(src,sizeof(char),sizeof(src),srcfp);
fwrite(src,sizeof(char),sizeof(src),dstfp);
memset(src,0,sizeof(src)); //清空缓冲区
}
fclose(srcfp);
fclose(dstfp);
printf("拷贝成功\n");
return 0;
}
效果图:
三、文件IO
对于标准IO而言,使用的是文件指针完成对文件的操作,文件指针中包含缓冲区和文件描述符,缓冲区为了降低系统调用的频率,使得IO操作效率更高,而文件描述符,主要是用于标准IO中调用文件IO函数使用的。
所以说,对于文件IO而言,直接通过文件描述符进行操作文件,每进行一次文件IO就会进行一次系统调用,进程就会从用户空间向内核空间进行一次切换,会使得进程进入休眠(阻塞)状态。导致程序效率较低。所以,能使用标准IO就尽量不要使用文件IO。
关于文件IO所涉及的函数:open、read、write、close、lseek
3.1 文件描述符
1> 文件描述符是一个大于等于0的整数,在使用open函数打开文件后,就会返回一个文件描述符,使用该文件描述符,就可以操作该文件的读写。
2> 在一个刚打开的程序中,默认会提供三个文件描述符,分别是0、1、2,对应的就是标注输入、标准输出、标准出错
3> 文件描述符的分配规则:最小未使用原则
4> 一个程序中文件描述符的个数默认是1024个(0--1023),可以通过指令ulimit -a 查看最大文件描述符
5> 特殊的文件描述符
3.2 打开文件(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:打开方式:如果参数2中的打开方式有创建文件操作,那么就必须写第三个参数,如果没有创建工作,就无需写第三个参数
O_RDONLY:只读的形式打开
O_WRONLY:只写的形式打开
O_RDWR:读写的形式打开
---------------------以上三种方式必须要包含其中一种,下面的是可选的-------------------------
O_CREAT:创建的形式打开,如果文件不存在,则创建文件
O_TRUNC:清空的形式打开,一般跟O_CREAT一起使用
O_APPEND:以追加的形式打开
O_EXCL:判断文件是否存在,如果存在则报错,如果文件不存在,则可以联合O_CREAT一起创建文件
如果有多个打开方式,中使用位或连接:
例如:"w": O_WRONLY|O_CREAT|O_TRUNC
"w+":O_RDWR|O_CREAT|O_TRUNC
"r":O_RDONLY
"r+":O_RDWR
"a":O_WRONLY|O_CREAT|O_APPEND
"a+":O_RDWR|O_CREAT|O_APPEND
参数3:创建权限,只有参数2中有O_CREAT的时候,才需要写第三个参数
文件最终的权限是:用户给的权限&~umask
一般普通文件的权限是0666 --> 0664,目录文件权限最大为0777 ->0775
3.3 关闭文件(close)
#include <unistd.h>
int close(int fd);
功能:关闭一个文件描述符
参数:要关闭的文件描述符
返回值:成功返回0,失败返回-1并置位错误码
#include<myhead.h>
int main(int argc, const char *argv[])
{
int fd; //定义文件描述符
if((fd = open("./hello.txt", O_WRONLY|O_CREAT|O_EXCL, 0664)) == -1)
{
//判断是什么原因出错
if(errno == EEXIST)
{
printf("文件已经存在,无需重新创建,直接打开即可\n");
fd = open("./hello.txt", O_WRONLY|O_TRUNC);
}else
{
perror("open error");
return -1;
}
}
//输出该文件的文件描述符
printf("fd = %d\n", fd); //3
//关闭文件
if(close(fd) == 0)
{
printf("文件关闭成功\n");
}else
{
perror("close error");
}
return 0;
}
3.4 文件IO的读写(read、write)
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
功能:向fd文件描述符关联的文件中,写入count字节的数据,这些数据起始地址为buf
参数1:要写入文件的文件描述符
参数2:数据的起始地址
参数3:数据大小
返回值:成功返回写入字节个数,失败返回-1并置位错误码
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
功能:从fd关联的文件中读取count字节的数据放入buf中
参数1:要写入文件的文件描述符
参数2:数据的起始地址
参数3:数据大小
返回值:成功返回写入字节个数,失败返回-1并置位错误码
练习:使用read、write完成两个文件的拷贝
#include<myhead.h>
int main(int argc, const char *argv[])
{
//判断传入文件个数
if(argc != 3)
{
printf("intput file error\n");
printf("usage:./a.out srcfile destfile\n");
return -1;
}
//定义两个文件描述符
int srcfd,destfd;
//以只读的形式打开第一个文件
if((srcfd=open(argv[1], O_RDONLY)) == -1)
{
perror("open srcfile error");
return -1;
}
//以只写的形式打开第二个文件
if((destfd=open(argv[2], O_WRONLY|O_CREAT|O_TRUNC, 0664)) == -1)
{
perror("open destfile error");
return -1;
}
//定义缓存区用来搬运数据
char buf[128] = "";
int ret = 0;
while((ret=read(srcfd, buf, sizeof(buf))) > 0)
{
//将读取的数据写入新文件中
write(destfd, buf, ret);
}
printf("拷贝成功\n");
//关闭文件
close(srcfd);
close(destfd);
return 0;
}
3.5 光标移动(lseek)
#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);
功能:移动光标位置
参数1:文件描述符
参数2:偏移量
可正:向后偏移
可负:向前偏移
可0:不偏移
参数3:偏移起始位置
SEEK_SET:从开头进行偏移
SEEK_CUR:从光标当前位置偏移
SEEK_END:从结尾进行偏移
返回值:光标所在位置:lseek = fseek + ftell
失败返回(off_t)-1并置位错误码
例如:lseek(fd, 0, SEEK_END) -->文件大小
练习:使用lseek读取图像的大小,并更改图像
#include<myhead.h>
int main(int argc, const char *argv[])
{
int fd; //定义文件描述符
if((fd=open("./kongkong.bmp", O_RDWR)) == -1)
{
perror("open error");
return -1;
}
//偏移2个字节得到图像大小
lseek(fd, 2, SEEK_SET);
unsigned int SIZE;
read(fd, &SIZE, sizeof(SIZE));
printf("SIZE = %u\n", SIZE);
unsigned char color[3] = {0,255,0}; //定义颜色
//重新定位光标
lseek(fd, 54, SEEK_SET);
for(int i=0; i<1000; i++) //外行
{
for(int j=0; j<1080; j++) //内列
{
write(fd, color, sizeof(color));
}
}
//关闭文件
close(fd);
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; /* 设备号信息e */
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; /* 设备Id (if special file) */
off_t st_size; /* 文件大小,以字节为单位 */
blksize_t st_blksize; /* 一个IO块的大小 */
blkcnt_t st_blocks; /* 块的个数 */
struct timespec st_atim; /* 最后一次获取的时间 */
struct timespec st_mtim; /* 最后一次更改权限时间 */
struct timespec st_ctim; /* 最后一次状态修改的时间 */
};
文件类型:
需要使用 st_mode & S_IFMT --->得到文件权限
S_IFMT 0170000 bit mask for the file type bit field
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
文件权限:
需要使用 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 当前用户的读、写、可执行
S_IRUSR 00400 当前用户读权限
S_IWUSR 00200 当前用户写权限
S_IXUSR 00100 当前用户可执行权限
S_IRWXG 00070 当前组的读、写、可执行
S_IRGRP 00040 当前组读权限
S_IWGRP 00020 当前组写权限
S_IXGRP 00010 当前组可执行权限
S_IRWXO 00007 其他用户的读、写、可执行
S_IROTH 00004 其他用户的读权限
S_IWOTH 00002 其他用户的写权限
S_IXOTH 00001 其他用户的可执行权限
#include<myhead.h>
int main(int argc, const char *argv[])
{
struct stat sb; //定义一个文件状态的结构体变量
//调用获取文件状态函数
stat("./", &sb);
//输出该文件的属性
printf("type = %#o\tmode = %#o\tsize = %ld\t inode = %ld\n",\
sb.st_mode&S_IFMT,\
sb.st_mode&0777,\
sb.st_size,\
sb.st_ino);
//判断文件类型
switch(sb.st_mode&S_IFMT)
{
case 0140000:
{
printf("这是一个套接字文件\n");
}
break;
case 0120000:
{
printf("这是一个链接文件\n");
}
break;
case 0100000:
{
printf("这是一个普通文件\n");
}
break;
case 0060000:
{
printf("这是一个块设备文件\n");
}
break;
case 0040000:
{
printf("这是一个目录文件\n");
}
break;
case 0020000:
{
printf("这是一个字符设备文件\n");
}
break;
case 0010000:
{
printf("这是一个管道文件\n");
}
break;
default:printf("文件有误\n");
}
return 0;
}
五、目录的操作
5.1 打开目录(opendir)
#include <sys/types.h>
#include <dirent.h>
DIR *opendir(const char *name);
功能:打开一个目录,并返回该目录的指针
参数:要打开的目录路径
返回值:成功返回该目录的起始地址,失败返回NULL,并置位错误码
5.2 关闭目录(closedir)
#include <sys/types.h>
#include <dirent.h>
int closedir(DIR *dirp);
功能:关闭一个目录文件
参数:目录文件指针
返回值:成功返回0,失败返回-1并置位错误码
5.3 读取目录下的文件内容(readdir)
#include <dirent.h>
struct dirent *readdir(DIR *dirp);
功能:从目录文件中读取一个文件的信息,将读取的文件信息返回
参数:目录指针
返回值:成功目录中文件的结构体指针,如果目录系统读取结束,会返回NULL,不置位错误码,如果读取失败,则返回NULL并置位错误码
struct dirent {
ino_t d_ino; /* Inode number */ inode号
off_t d_off; /* Not an offset; see below */ 偏移量
unsigned short d_reclen; /* Length of this record */ 该记录的长度,结构体的长度
unsigned char d_type; /* Type of file; not supported 文件类型
by all filesystem types */
char d_name[256]; /* Null-terminated filename */ 文件名
};
#include<myhead.h>
int main(int argc, const char *argv[])
{
//定义目录指针
DIR *dp;
//打开一个目录
if((dp = opendir("./")) == NULL)
{
perror("opendir error");
return -1;
}
//定义遍历指针,将所有文件进行遍历
struct dirent *q = NULL;
while((q=readdir(dp)) != NULL)
{
//输出当前遍历到的文件信息
printf("name = %10s\tinode = %ld\tsize = %d\t, type = %#o\t",\
q->d_name, q->d_ino, q->d_reclen, q->d_type);
switch(q->d_type)
{
case DT_BLK:
{
printf("块设备文件\n");
}
break;
case DT_CHR:
{
printf("字符设备文件\n");
}
break;
case DT_DIR:
{
printf("目录文件\n");
}
break;
case DT_FIFO:
{
printf("管道文件\n");
}
break;
case DT_LNK:
{
printf("链接文件\n");
}
break;
case DT_REG:
{
printf("普通文件\n");
}
break;
case DT_SOCK:
{
printf("套接字文件\n");
}
break;
default:printf("不知名文件\n");
}
}
//关闭目录
closedir(dp);
return 0;
}