IO【3】(预定义流,时间函数和文件类型及权限)

329 阅读8分钟

​ “这是我参与8月更文挑战的第14天,活动详情查看:8月更文挑战

预定义流

在程序开始之前,创建三个文件描述符,分别0、1、2对应的标准输入,标准输出,标准错误输出,同时也在其基础上封装出了三个预定义流指针

  1. 标准输入: stdin ----》键盘文件
  2. 标准输出: stdout ----》终端文件
  3. 标准错误输出:stderr ----》终端文件

代码:

int main(int argc, const char *argv[])
{
    //stdin
    char buf[123]={0};
    fgets(buf,123,stdin);//从标准输入流中读取数据
    printf("buf = %s\n",buf);
    //标准输出
    fprintf(stdout,"hello world%s","112123");
    //标准错误输出
    fprintf(stderr,"hello world%s","112123");
    return 0;
}
linux@ubuntu:~/demo/test/IO/test$ ./a.out 
hello world
buf = hello world

hello world112123hello world112123linux@ubuntu:~/demo/test/IO/test$ 

缓冲区

《面试题》:缓冲区是什么?标准IO在文件IO的基础上封装的一片存放数据的地址(一般用来存放不着急的数据),等到缓冲区这个地址中的数据存满,或者说程序员手动刷新这片空间,空间中的数据会被调用文件IO操作。

缓冲区一共分为三大类:

全缓冲

一般为对文件的操作,全缓冲区大小为4096个字节

(附加:tail -f + 文件名):可以另开一个终端动态查看文件内容

刷新缓冲区的条件

  1. 缓冲区满刷新缓冲区

  2. 程序结束刷新缓冲区

  3. 程序手动刷新缓冲区

fflush

  1. 原型:int fflush(FILE *stream);
  2. 功能:刷新缓冲区
  3. 参数:目标文件流指针
  4. 返回值:
    1. 成功:0
    2. 返回:EOF

测试代码:

int main(int argc, const char *argv[])
{
    FILE * fp = fopen("./1.txt","w");
    //fopen、open的打开方式只针对与打开时的一瞬间
    if (NULL ==fp)
    {
        perror("fopen");
        return -1;
    }
    fprintf(fp,"hello world");
    fflush(fp);
    while(1);
    
    return 0;
}         

行缓冲

只有两个是行缓冲,标准输入,标准输出。行缓冲大小为1024个字节

刷新行缓冲的条件:

  1. 缓冲区满则刷新缓冲区

  2. 缓冲区遇到\n则刷新缓冲区

  3. 标准输入标准输出一方要使用缓冲区时,正在使用的一方需要让出缓冲区,给需要用的一方使用

  4. fflush刷新缓冲区

  5. 程序结束

测试代码:

1.验证缓存区大小

#include <stdio.h>
int main(int argc, const char *argv[])
{
    //航缓冲大小为1024个字节

    //验证缓冲区大小
    int i = 0;
    for ( i = 0; i < 1024; i++)
    {
        printf("1");//每次向标准输出输入1个字节
    }
    while(1);//起阻塞作用
    
    return 0;
}

linux@ubuntu:~/demo/test/IO/test$ ./a.out 
^C
//1024时不会打印
linux@ubuntu:~/demo/test/IO/test$ ./a.out 
111111111111......1111111111111111
//1025时可以打印

2.验证'\n'可以刷新行缓存区

#include <stdio.h>
int main(int argc, const char *argv[])
{
    //验证'\n'刷新缓存区
    int i = 0;
    for ( i = 0; i < 10; i++)
    {
        printf("1");//每次向标准输出输入1个字节
    }
    printf(\n"");//刷新
    while(1);//阻塞
    
    return 0;
}
linux@ubuntu:~/demo/test/IO/test$ ./a.out 
1111111111

3.验证标准输入输出刷新缓存区

#include <stdio.h>
int main(int argc, const char *argv[])
{
    //验证'\n'刷新缓存区
    int i = 0;
    for ( i = 0; i < 10; i++)
    {
        printf("1");//每次向标准输出输入1个字节
    }
    int n;
    scanf("%d",&n);
    /*printf("hello");
    return 0;
}
linux@ubuntu:~/demo/test/IO/test$ ./a.out 
11111111112

linux@ubuntu:~/demo/test/IO/test$ ./a.out 
1111111111hellolinux@ubuntu:~/demo/test/IO/test$

4.ffiush也可以,就不上代码了。

无缓冲

一般为标准错误输出,一般用于比较着急的数据,所以不会进入缓冲区。直接调用文件IO。

一般使用方式:fprintf(stderr,"hello world");

注意:自己做错误报告,没有错误码时,后续报错信息:使用这种方式,避免printf忘记写'\n'没有输出;

时间函数

1.time

  1. 头文件:#include <time.h>

  2. 原型:

    time_t time(time_t *tloc);

    //以后要指针时,可以定义变量取地址放在这

    time_t mytime;
    time(mytime);
    
    struct tm *localtime(const time_t *timep); 
    struct tm * mytm = localtime(&mytime);
    

    typedef long time_t

  3. 功能:获取现在计算机时间到1970-01-01-00-00-00的秒数

  4. 参数:

    tloc:用来存放获取到的描述的变量的地址,该值可以填写NULL ,填写NULL的话获取秒数通过返回值来获取

  5. 返回值:

    1. 成功返回1970-01-01-00-00-00到现在的秒数
    2. 失败返回:(time_t)-1

代码:

#include <stdio.h>
#include <time.h>
int main(int argc, const char *argv[])
{  
    time_t mytime;
    mytime = time(NULL);
    printf("当前时间到1970年的时间为%ld\n",mytime);
    return 0;
}       
linux@ubuntu:~/demo/test/IO/test$ ./a.out 
当前时间到1970年的时间为1630154859
//利用秒数就可以算出时间

2.ctime

  1. 原型:char *ctime(const time_t *timep);
  2. 功能:按照固定的格式将现在的时间转换出来
  3. 参数:time_t类型的变量的地址
  4. 返回值:
    1. 成功返回封装好的字符串的首地址
    2. 失败返回:NULL
  5. 注意:使用ctime时输出的格式时固定,用户无法更改

代码:

#include <stdio.h>
#include <time.h>
int main(int argc, const char *argv[])
{  
	time_t mytime;
	mytime = time(NULL);//time函数获取到秒数
	printf("当前时间到1970年的时间为%ld\n",mytime);
	//使用ctime将秒数转换成固定的字符串
	char * buf = ctime(&mytime);
	printf("现在的时间为%s",buf);
    return 0;
}  

linux@ubuntu:~/demo/test/IO/test$ ./a.out 
当前时间到1970年的时间为1630155367
现在的时间为Sat Aug 28 05:56:07 2021

3.localtime

  1. 原型:struct tm *localtime(const time_t *timep);
  2. 功能:会将timep转换成现在的时间,将各项数据存放之结构中
  3. 参数:秒数变量的地址
  4. 返回值:
    1. 成功:会返回一个已经封装好的tm结构体
    2. 失败:NULL

suruct tm结构体:

struct tm {
	int tm_sec;    /* Seconds (0-60) 有1秒为闰秒用于时间校准*/
	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) 使用时需要+1 */
	int tm_year;   /* Year - 1900  = 70 -使用时需要 + 1900-*/万年虫
	int tm_wday;   /* Day of the week (0-6, Sunday = 0) */
	int tm_yday;   /* Day in the year (0-365, 1 Jan = 0) */
	int tm_isdst;  /* 夏令营时间 */
};     

代码:

#include <time.h>

int main(int argc, const char *argv[])
{
    time_t mytime;
    mytime = time(NULL);//time函数获取到秒数

    //调用localtime函数进行tm结构体封装
    struct tm * mytm = localtime(&mytime);
    if(NULL == mytm)
    {
        perror("localtime");
        return -1;
    }
    printf("当前的时间为:%d年-%d月-%d日--%d:%d:%d\n",
    mytm->tm_year+1900,mytm->tm_mon+1,mytm->tm_mday,
    mytm->tm_hour,mytm->tm_min,mytm->tm_sec);
    return 0;
}

📝练习:编写日志文件,将时间每隔1秒中写入文件之中,sleep(1)

#include <stdio.h>
#include <time.h>
int main(int argc, const char *argv[])
{
    //打开一个文件
    FILE *fp = fopen("./1.txt","a");
    if(NULL == fp)
    {
        perror("fopen");
        
        return -1;
    }
    while(1)
    {
        //获取当前的距离1970年的秒数
        time_t mytime = time(NULL);
        //开始转换时间
        struct tm * mytm = localtime(&mytime);
        fprintf(fp,"当前的时间为:%d年-%d月-%d日--%d:%d:%d\n",
        mytm->tm_year+1900,mytm->tm_mon+1,mytm->tm_mday,
        mytm->tm_hour,mytm->tm_min,mytm->tm_sec);
        fflush(fp);
        sleep(1);
    }
    return 0;
} 

stat获取文件状态属性

  1. 头文件:
    1. #include <sys/types.h>
    2. #include <sys/stat.h>
    3. #include <unistd.h>
  2. 原型:int stat(const char *pathname, struct stat *statbuf);
  3. 功能: 获取文件的状态属性
  4. 参数:
    1. pathname:目标文件的路径及名称
    2. statbuf :存放文件属性信息的结构体地址
  5. 返回值:
    1. 成功返回:0
    2. 失败返回:-1

stat 结构体

struct stat {
	dev_t     st_dev;         /* 设备文件ID号*/
	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号 */
	off_t     st_size;        /* 文件的大小 */
	blksize_t st_blksize;     /* Block size for filesystem I/O */
	blkcnt_t  st_blocks;      /* Number of 512B blocks allocated */
	struct timespec st_atim;  /* 最后一次访问的时间 */
	struct timespec st_mtim;  /* 最后一次修改的时间 */
	struct timespec st_ctim;  /* 最后一次状态改变的时间 */

	#define st_atime st_atim.tv_sec      /* Backward compatibility */
	#define st_mtime st_mtim.tv_sec
	#define st_ctime st_ctim.tv_sec
};
           
struct timespec {
	time_t      tv_sec;     /* 秒数 */
	long        tv_nsec;    /* 纳秒 */
};

文件的类型

st_mode

rWXRWxRWX
876543210
宿主组员其他
876543210

文件类型定义了以下掩码值:

st_mode有文件的信息,和0170000相与会一一对应。

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

文件权限定义了以下掩码值:

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   owner has read, write, and execute permission
S_IRUSR     00400   owner has read permission
S_IWUSR     00200   owner has write permission
S_IXUSR     00100   owner has execute permission

S_IRWXG     00070   group has read, write, and execute permission
S_IRGRP     00040   group has read permission
S_IWGRP     00020   group has write permission
S_IXGRP     00010   group has execute permission

S_IRWXO     00007   others  (not  in group) have read, write, and execute permission
S_IROTH     00004   others have read permission
S_IWOTH     00002   others have write permission
S_IXOTH     00001   others have execute permission

📝打印文件类型和权限

代码:

#include <stdio.h> 
#include <sys/types.h> 
#include <sys/stat.h> 
#include <unistd.h> 
#include <sys/types.h> 
#include <pwd.h> 
#include <grp.h> 
#include <time.h>

int main(int argc, const char *argv[])
{
    struct stat mystat;

    if(stat(argv[1],&mystat) == -1)
    {
        perror("stat");
        return -1;
    }
    //开始打印文件的类型
    switch (mystat.st_mode & S_IFMT)
    {
    case S_IFSOCK:
        printf("s");
        break;
    case S_IFLNK:
        printf("l");
        break;
    case S_IFREG:
        printf("-");
        break;
    case S_IFBLK:
        printf("b");
        break;
    case S_IFDIR:
        printf("d");
        break;
    case S_IFCHR:
        printf("c");
        break;
    case S_IFIFO:
        printf("p");
        break;
    }

    //开始打印文件的权限
    if((mystat.st_mode & S_IRUSR) !=0)
    {
        printf("r");
    }else{
        printf("-");
    }
    if((mystat.st_mode & S_IWUSR) !=0)
    {
        printf("w");
    }else{
        printf("-");
    }
    if((mystat.st_mode & S_IXUSR) !=0)
    {
        printf("x");
    }else{
        printf("-");
    }
    //开始判断组权限
    if((mystat.st_mode & S_IRGRP) !=0)
    {
        printf("r");
    }else{
        printf("-");
    }
     if((mystat.st_mode & S_IWGRP) !=0)
    {
        printf("w");
    }else{
        printf("-");
    }
    if((mystat.st_mode & S_IXGRP) !=0)
    {
        printf("x");
    }else{
        printf("-");
    }
    //开始判断其他用户权限
     if((mystat.st_mode & S_IROTH) !=0)
    {
        printf("r");
    }else{
        printf("-");
    }
     if((mystat.st_mode & S_IWOTH) !=0)
    {
        printf("w");
    }else{
        printf("-");
    }
    if((mystat.st_mode & S_IXOTH) !=0)
    {
        printf("x");
    }else{
        printf("-");
    }
    printf("\n");
    
    return 0;
}
linux@ubuntu:~/demo/test/IO/test$ ./a.out test.c 
-rwxrwxrwx  

用户的信息

  1. 头文件:
    1. #include <sys/types.h>
    2. #include <pwd.h>
  2. 原型:struct passwd *getpwuid(uid_t uid);
  3. 功能:根据ID号查询用户信息然后封装结构体
  4. 参数:用户ID号
  5. 返回值:
    1. 成功返回已经封装好的结构体passwd
    2. 失败返回NULL
struct passwd {
	char   *pw_name;       /* username */
	char   *pw_passwd;     /* user password */
	uid_t   pw_uid;        /* user ID */
	gid_t   pw_gid;        /* group ID */
	char   *pw_gecos;      /* user information */
	char   *pw_dir;        /* home directory */
	char   *pw_shell;      /* shell program */
};   

组的信息

  1. 头文件:
    1. #include <sys/types.h>
    2. #include <grp.h>
  2. 原型:struct group *getgrgid(gid_t gid);
  3. 参数:组ID号
  4. 返回值:
    1. 成功返回已经封装好的结构体地址
    2. 失败返回NULL
struct group {
	char   *gr_name;        /* group name */
	char   *gr_passwd;      /* group password */
	gid_t   gr_gid;         /* group ID */
	char  **gr_mem;         /* 以NULL结尾的一个数组,里面存放的是组员的名字 */
};        

代码:

   //补在上面代码后面就行了

	//链接数
    printf(" %ld ",mystat.st_nlink);

    //打印用户名

    struct passwd * mypass = getpwuid(mystat.st_uid);
    if(NULL ==mypass)
    {
        perror("getpwuid");
        return -1;
    }

    printf("%s ",mypass->pw_name);
    //打印组名
    struct group * mygr = getgrgid(mystat.st_gid);
    if (NULL ==mygr)
    {
        perror("getgrgid");
        return -1;
    }
    //打印组命
    printf("%s ",mygr->gr_name);
    //打印大小
    printf("%ld",mystat.st_size);
    //打印时间
    struct tm * mytm = localtime(&mystat.st_ctim.tv_sec);
    printf(" %d月  %d %02d:%02d",mytm->tm_mon+1,mytm->tm_mday,mytm->tm_hour,mytm->tm_min);
    printf("%s\n",argv[1]); 
linux@ubuntu:~/demo/test/IO/test$ ./a.out test.c 
-rwxrwxrwx 1 linux linux 2537 828 07:31test.c

目录操作

opendir

  1. 头文件:
    1. #include <sys/types.h>
    2. #include <dirent.h>
  2. 原型:
    1. DIR *opendir(const char *name);
    2. DIR :目录流指针-----打开目录的标志,共给后续操作使用
  3. 参数:name:要开的的目录的路径
  4. 反回值:
    1. 成功返回目录流指针
    2. 失败返回 NULL

readdir

  1. 头文件: #include <dirent.h>

  2. 原型:struct dirent *readdir(DIR *dirp);

  3. 功能:读取目录文件

  4. 参数:目标目录流指针

  5. 返回值:

    1. 成功返回一个已经封装好的目录信息结构体的地址
    2. 失败返回NULL
    3. 读到文件末尾返回NULL
  6. 注意:读取时以文件为单位,不能一起将所有文件信息读出来,只能依次读取 每次读取都会返回一个封装好的结构体。

struct dirent {
ino_t          d_ino;       /* Inode号用于索引列表使用--文件的编号 */
off_t          d_off;       /* 文件的偏移值 */
unsigned short d_reclen;    /* 文件名字的长度 */
unsigned char  d_type;      /* 文件的类型*/
char           d_name[256]; /* 以NULL为终止字符的文件名字符串 */
};

closedir

  1. 头文件:
    1. #include <sys/types.h>
    2. #include <dirent.h>
  2. 原型:int closedir(DIR *dirp);`
  3. 功能:关闭一个目录流指针
  4. 参数:目标目录流指针
  5. 返回值:
    1. 成功返回0 ;
    2. 失败返回-1;

代码:

#include <stdio.h>
#include <dirent.h>
#include <sys/types.h>

int main(int argc, const char *argv[])
{
    //打开一个目录
    DIR *mydir =  opendir(".");
    if(NULL == mydir)
    {
        perror("opendir");
        return -1;
    }
    //开始读取文件
    struct dirent * mydirent; //要顶一个struct dirent *指针接收read返回值
    while(NULL != (mydirent = readdir(mydir)))
    {
        printf("文件名为 %s\n",mydirent->d_name);

    }
    closedir(mydir);
    return 0;
}  
linux@ubuntu:~/demo/test/IO/test$ ./a.out 
文件名为 .
文件名为 test.c
文件名为 a.out
文件名为 core
文件名为 ..
文件名为 1.txt

📝练习:

实现ls -l 加参数和不加参数都可以