2023/8/16 -- IO

 作业:

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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值