IO

IO简介

【1】什么是IO

#include <stdio.h>
std:standard 标准的
i:input 输入 数据从外部存储设备输入到内存中
o:output 输出 数据从内存输出到外部存储设备中。

存储设备:
​	1.硬盘:机械硬盘和固态硬盘
​	2.内部存储设备:内存:SDROM DDR4 	用于缓存硬盘上的数据。

总结:IO就是数据从硬盘到内存,内存到硬盘的流动

【2】IO的分类

1)文件IO

文件IO:由系统提供的基本IO函数,与操作系统绑定的,又称之为系统调用(从用户空间到内核通过系统调用实现)。
​使用文件IO每输入一次数据,CPU模式会切换一次。
例子:

| windos                | linux       |
| file_read  file_write | read  write |

注意
1.文件IO的复用性低
2.文件IO涉及到用户空间到内核空间的切换,cpu模式切换,C代码调用汇编指令等等,属于以一种耗时操作,应该尽量减少文件IO的使用。

2)标准IO

标准IO是根据ANSI标准,对文件IO进行了二次封装,比如:scanf,printf等
特点
1.提高代码的可移植性和复用性;
2.提高输入输出的效率
设置了一个缓冲区,缓冲区满或者满足一定条件后,调用文件IO,陷入内核空间,由内核完成对硬件的操作,大大减少了对文件IO的调用。下图为标准IO和文件IO的对比。
标准IO和文件IO的对比

2.标准IO

【1】流和流指针

流(stream):将数据一个一个的移入或者移出文件的形式,叫做字节流。
流指针(FILE*):每打开一个文件,就会在内存中申请一片空间(缓冲区),管理这片空间的变量都存储在FILE结构体,FILE结构体是由系统定义的,我们只要拿过来用就可以了。

1. 查看FILE结构体成员

 第一步:$ vi 	-t 	FILE 			
 //vi -t 可以用于查看系统提供的数据类型,结构体,宏定义,全局变量
 第二步:输入1 
  # pri kind tag       file
  1 F   t   FILE      /usr/include/stdio.h
          typeref:struct:_IO_FILE
          typedef struct _IO_FILE FILE;
 第三步:找到typedef struct __IO_FILE
 第四步:$ vi 	-t 	__IO_FILE
 第五步:输入1
  # pri kind tag       file
  1 F   s  _IO_FILE   /usr/include/libio.h
          struct _IO_FILE {
 //或者在第三步鼠标悬浮在 _IO_FILE上时,ctrl+鼠标右键直接进入,进行第五步
struct _IO_FILE {
  int _flags;    /* High-order word is _IO_MAGIC; rest is flags. */
#define _IO_file_flags _flags
/*The following pointers correspond to the C++ streambuf protocol. */
/* Note:Tk uses the _IO_read_ptr and _IO_read_end fields directly. */
  char* _IO_read_ptr;   /* Current read pointer */
  char* _IO_read_end;   /* Start of get area.*/
  char* _IO_read_base;  /* End of get area.*/
  char* _IO_write_base; /* Start of put area. */
  char* _IO_write_ptr;  /* Current put pointer. */
  char* _IO_write_end;  /* End of put area. */
  char* _IO_buf_base;   /* 缓冲区的起始地址 */
  char* _IO_buf_end;    /* 缓冲区的结束地址 */
  /* The following fields are used to support backing up and undo. */
  char *_IO_save_base; /*Pointer to start of non-current get area. */
  char *_IO_backup_base;/*Pointer to first valid character of backup area*/
  char *_IO_save_end; /* Pointer to end of non-current get area. */
  struct _IO_marker *_markers;
  struct _IO_FILE *_chain;
  int _fileno;     /*文件描述符*/

2. 标准输入输出流

在main函数启动之前,会默认打开三个流指针。

stdin 	标准输入		从终端获取数据
​stdout 	标准输出	    将数据输出到终端
​stderr 	标准错误输出
​通过 vi -t查看
/* Standard streams.  */
extern struct _IO_FILE *stdin;   /* Standard input stream.  */
extern struct _IO_FILE *stdout;     /* Standard output stream.  */
extern struct _IO_FILE *stderr;     /* Standard error output stream.  */
/* C89/C99 say they're macros.  Make them happy.  */
#define stdin stdin
#define stdout stdout
#define stderr stderr

【2】标准IO函数

1.常见的标准IO函数

fopen  /  fclose 	打开/关闭一个文件;
fprintf  /  fscanf 	将数据输入到文件中/从文件中获取数据
fputc   /  fgetc 	将单个字符的输入到文件中/从文件中读取字符
fputs  / 	fgets 	将字符串输入到文件中/从文件中读取字符串
fwrite  / fread 	将数据以二进制的形式输出到文件中 / 从文件中读取二进制数据;
fseek 	偏移流指针

2. man手册

$ man man 查看man手册

       1   Executable programs or shell commands
       2   System calls (functions provided by the kernel)
       3   Library calls (functions within program libraries)  
man 1 卷 	linux中的可执行文件或shell指令
man 2 卷	系统调用(文件IO)
man 3 卷	库调用(标准IO)

man查找的时候,会默认从第一卷开始查找

3. 标准IO函数的使用

1)fopen
功能:打开一个文件;
头文件:
       #include <stdio.h>
原型:
       FILE *fopen(const char *path, const char *mode);
参数:
    char *path:指定要打开的文件路径+文件名(eg:"./1.c");
	char *mode:打开方式(eg:"r+");
       r      以读的方式打开;
           		如果文件不存在,则打开失败;
       r+     以读写的方式打开;
				如果文件不存在,则打开失败;
       w      以写的方式打开;
				如果文件不存在,则创建文件;
				如果文件存在,则清空文件;
       w+     以读写的方式打开;
				如果文件不存在,则创建文件;
				如果文件存在,则清空文件;
       a      以写的方式打开;
				如果文件不存在,则创建文件;
				如果文件存在,则以追加的方式写;
       a+     以读写的方式打开;
				如果文件不存在,则创建文件;
				如果文件存在,则以追加的方式写;
返回值:
    成功,返回打开的文件流指针;
	失败,返回NULL,同时更新错误码errno;
2)perror
功能:通过错误码errno,打印错误信息;
头文件:
       #include <stdio.h>
原型:
       void perror(const char *s);
参数:
    char *s:用于提示的字符串(eg:"fopen")
#include <errno.h>
    int errno; 本质上是一个全局变量,对文件进行操作的时候,会出现各种错误,例如文件不存在,文件权限不足
    errno中已经定义好了各种数值,与错误相对应;
errno路径:/usr/include/asm-generic 目录下 errno.h errno-base.h
3)fclose
功能:关闭一个文件;
头文件:
       #include <stdio.h>
原型:
       int fclose(FILE *fp);
参数:
    FILE *fp:要关闭的流指针;
返回值:
    成功,返回0;
	失败,返回EOF(其实就是-1),更新errno
思考:能否用一个流指针对文件进行重复fopen和fclose

答案:不能
​ 1.调用fopen时,会去堆空间申请一片缓冲区,返回的流指针中就存储了这片空间的首地址。
​ 2.不要重复打开一个文件,会造成资源浪费,而且有可能会造成内存泄漏,linux内核文件的打开次数是有限的。
​ 3.调用fclose时,会通过free释放指定的堆空间,再次调用fclose,会造成重复释放。

例子
#include <stdio.h>
#include <errno.h>
int main(int argc, const char *argv[])
{
	if(argc < 2)
	{
		printf("参数不足,请输入打开方式\n");
		return -1;
	}
	FILE* fp = fopen("./fopen.txt", argv[1]);
	if(NULL == fp)
	{
		printf("%d\n", errno);
		perror("fopen");
		printf("打开失败\n");
		return -1;
	}
	printf("打开成功\n");
	if(fclose(fp)<0)
	{
		perror("fclose");
		return -1;
	}
	printf("关闭成功\n");
	return 0;
}
4)fprintf
功能:将数据格式化输出到文件中;
头文件:
       #include <stdio.h>
原型:
       int printf(const char *format, ...);
       int fprintf(FILE *stream, const char *format, ...);
参数:
    FILE *stream:要输出到文件的文件流指针;
	char *format:格式化输出;
	...:不定参数;
返回值:
    成功,返回输出的数据个数,字节为单位
    失败,返回负数,更新errno;
例子
//2.输出到文件中
	if(fprintf(fp, "hello world %d", __LINE__)<0)
	{
		perror("fprintf");
		return -1;
	}
5)fscanf
功能:从文件中格式化获取数据;
头文件:
       #include <stdio.h>
原型:
       int scanf(const char *format, ...);
       int fscanf(FILE *stream, const char *format, ...);
参数:
    FILE *stream:需要获取数据的文件流指针;
	char *format:格式化输入;
	...:不定参数;
返回值:
    成功,返回获取到的数据个数;
    失败或者读到文件结尾,返回EOF,更新errno;

注意
1.fscanf和scanf类似,遇到空格,TAB,回车等均会停止读入
2.fscanf读到文件结尾时返回EOF

例子
//1.打开文件
	FILE *fp = fopen("./fprintf.txt", "r");
	if(NULL == fp)
	{
		perror("fopen");
		return -1;
	}
	//2.文件中读取数据
	char buf[15] = "";
	char buf1[15] = "";
	fscanf(fp, "%s %s", buf, buf1);
	printf("buf = %s %s\n", buf, buf1);
	int res = fscanf(fp, "%s %s", buf, buf1);
	printf("res = %d\n",res);
	if(res < 0)
	{
		perror("fscanf");
		return -1;
	}
	//3.关闭文件
	fclose(fp);
	return 0;
6)fputc
功能:将单个字符输出到文件中;
头文件:
       #include <stdio.h>
原型:
       int fputc(int c, FILE *stream);
参数:
    int c:指定要输出的字符;
	FILE *stream:需要写入的文件的流指针;
返回值:
    成功,返回输出字符的ascii值;
	失败,返回EOF,更新errno;
例子
	fputc(97, fp);
	fputc('a', fp);
    int c = 'a';
	if(fputc(c, fp) < 0)
    {
    	perror("fputc");
        return -1;
    }
7)fgetc
功能:从文件中获取单个字符;
头文件:
       #include <stdio.h>
原型:
       int fgetc(FILE *stream);
参数:
    FILE *stream:要从文件获取的文件流指针;
返回值:
    成功,返回获取到的字符;
    失败,或者读到文件末尾时返回EOF,更新errno;

注意
1.读到文件末尾时返回EOF
2.区别getchar和fgets:

char c=getchar();
int c=fgetc(fd);
例子
int c = 0;
    c = fgetc(fp);
	if(c == EOF)
    {
    	perror("fgetc");
        return -1;
    }
小练习
1.用fgetc函数,计算文件大小。
2.写一个函数,计算一个文件中共有几行。
​	提示:(判断文件中有几个‘\n’,就算文件结尾也会有一个'\n'字符)
3.使用fgetc和fputc实现文件拷贝,例如将a.c拷贝成b.c

第1题

1.用fgetc函数,计算文件大小。
#include <stdio.h>
int file_size(FILE* fp)
{
    int size = 0;
    //循环读取文件字符,直到返回错误
    /*
    while(fgetc(fp)!=EOF)
        size++;
    */
while(1)                                                                       
    {
        int c = fgetc(fp);
        if(c == EOF)
        {
            break;
        }
        size++;
    }
    return size;
}
int main(int argc, const char *argv[])
{
    //1.外部传参传入文件名
    if(argc < 2)
    {
        printf("请输入文件\n");
        return -1;
    }
    FILE *fp = fopen(argv[1], "r");
    if(NULL == fp)
    {
        perror("fopen");
        return -1;
    }
    int size = file_size(fp);
    printf("%d\n",size);
    fclose(fp);
    return 0;
}

第2题

2.写一个函数,计算一个文件中共有几行。
​	提示:(判断文件中有几个‘\n’,就算文件结尾也会有一个'\n'字符);
//获取文件行数
int file_line(FILE* fp)
{
    int line = 0;
    int c = 0;
    while((c = fgetc(fp)) != EOF)
    {
        if('\n' == c)
        {
            line++;
        }    
    }
    return line;
}
int main(int argc, const char *argv[])
{
    //1.外部传参传入文件名
    if(argc < 2)
    {
        printf("请输入文件\n");
        return -1;
    }
    FILE *fp = fopen(argv[1], "r");
    if(NULL == fp)
    {
        perror("fopen");
        return -1;
    }
    int line = file_line(fp);
    printf("line=%d\n", line);
    fclose(fp);
    return 0;
}

第3题

3.使用fgetc和fputc实现文件拷贝,例如将a.c拷贝成b.c;
#include <stdio.h>
void file_copy(FILE* rp, FILE* wp) 
{
    char c = 0;
    while((c=fgetc(rp)) != -1) 
    {   
        fputc(c, wp);
    }   
}
int main(int argc, const char *argv[])
{                                  
    if(argc < 2)
    {   
        return -1; 
    }   
    FILE* fp_r = fopen(argv[1], "r");
    if(NULL == fp_r)
    {   
        perror("fopen");
        return -1; 
    }   
    FILE* fp_w = fopen("./b.c", "w");
    file_copy(fp_r, fp_w);
    fclose(fp_r);
    fclose(fp_w);
    return 0;
}
8)缓冲区

标准IO的内容都是存储在缓冲区中,然后输出到硬件中。

1.全缓冲
  • 操作对象
    对普通文件进行操作(通过fopen打开的文件),通过FILE*fp流指针维护一个4K大小的缓冲区;即文件打开的默认方式为全缓冲
  • 大小:
    4K = 4*1024 = 4096byte;
    fp->_IO_buf_end - fp->_IO_buf_base = 4096byte
    
  • 刷新缓冲区
    1)缓冲区满
    2)用fflush函数强制刷新缓冲区
           #include <stdio.h>
           int fflush(FILE *stream);
    
    3)关闭流指针
    4)程序正常退出
    5)调用exit函数
#include <stdio.h>
int main(int argc, const char *argv[])
{
    FILE *fp = fopen("./fullbuf.txt", "w");

    //1.缓冲区满
/******************************/
    int i = 0 ;
    while(i++<4096+1)
    {
        fputc('a', fp);
    }
/******************************/
    //2.用fflush函数
/******************************/
    fputc('b', fp);
    fputc('b', fp);
    fputc('b', fp);
    fflush(fp);
/******************************/
    //3.fclose
/******************************/
    fputc('c', fp);
    fputc('c', fp);
    fputc('c', fp);
    fclose(fp);
/******************************/
    //4.程序正常退出 return 0;
/******************************/
    fputc('d', fp);
    fputc('d', fp);
    fputc('d', fp);
    return 0;
/******************************/   
    //5.exit
/******************************/                                
    fputc('e', fp);
    fputc('e', fp);
    fputc('e', fp);
    exit(100);
    while(1);    
    fclose(fp);
    return 0;
}
2.行缓冲
  • 操作对象
    标准输入(stdin),标准输出(stdout)
  • 大小
    1K = 1024byte
    stdin->_IO_buf_end - stdin->_IO_buf_base = 1024byte
    
  • 刷新缓冲区
    1)缓冲区满
    2)用fflush函数强制刷新缓冲区
           #include <stdio.h>
           int fflush(FILE *stream);
    
    3)关闭流指针
    4)程序正常退出
    5)调用exit函数
    6)遇到‘\n’字符
    注意
    1.只有标准输入/输出(stdin和stdout才可以使用行缓冲)
    2.在全缓冲的基础上,行缓冲多了一种用换行符’\n’刷新缓冲区的方式
    3.由于系统优化,如果只是打开文件,不操作,并不会真正的申请空间。
3.无缓冲
  • 操作对象
    标准错误输出,stderr
    其实perror调用的就是stderr;
  • 缓冲区大小为0
9)fputs
功能:将字符串输出到文件中;
头文件:
    #include <stdio.h>
原型:
       int fputs(const char *s, FILE *stream);
参数:
    char *s:要输出的字符串首地址;
	FILE *stream:要输入到哪个文件的文件流指针;
返回值:
    成功,返回非负数;
    失败,返回EOF,更新errno
例子
FILE* fp = fopen("./fgets.txt", "w+");
	if(NULL == fp)
	{
		perror("fopen");
		return -1;
	}
	fputs("hello\n", fp);
	fputs("world\n", fp);
	fclose(fp);
10)fgets
功能:从文件中获取字符串(一行一行地读到缓冲区中);
头文件:
原型:
       char *fgets(char *s, int size, FILE *stream);
参数:
    char *s:保存获取到的字符串;
	int size:从文件中读取 size-1 个字节,因为要保留'\0'的位置;
		注意:如果一行不足size-1个字符,则读取个数为一行的实际个数。
	FILE *stream;
返回值:
    成功,返回char *s;
	失败,返回NULL;更新errno;

注意:
1.读取到文件末尾时,返回NULL
2.没有遇到\n,但长度达到size-1也会默认为一行,此时应判断是否读取了一行(判断结束符的前一位是否是换行符)即:buf[strlen(buf)-1]==’\n’

例子
char buf[20] = "";
	fp = fopen("./fgets.txt", "r");
	fgets(buf, 20-1, fp);
	printf("%s\n",buf);
小练习
1.写一个函数,计算一个文件中共有几行。 用fgets实现
​提示:(判断文件中有几个‘\n’,就算文件结尾也会有一个'\n'字符)
2.用fgets和fputs实现文件的拷贝,将a.c的内容拷贝到b.c中

第1题

1.写一个函数,计算一个文件中共有几行。 用fgets实现
提示:(判断文件中有几个‘\n’,就算文件结尾也会有一个'\n'字符);
#include <stdio.h>
#include <string.h>
int file_line(FILE* fp)
{
    int line = 0;
    char s[10] = "";
    //fgets读取到末尾时返回NULL
    while(fgets(s, 10-1, fp) != NULL)
    {
        int i = 0;
        //判断当达到size-1长度时是否是完整的一行,若是则行数加1
        //方法1:
        for(i=0; i<strlen(s); i++)
        {           
            if('\n' == s[i])
            {
                line++;
            }
        }
        //方法2:
        if(s[strlen(s)-1]=='\n')
        {
        	line++;
		}
        bzero(s, strlen(s));
    }
    return line;
}
int main(int argc, const char *argv[])
{
    if(argc < 2)
    {
        return -1;
    }
    FILE* fp = fopen(argv[1], "r");
    if(NULL == fp)
    {
        perror("fopen");
        return -1;
    }
    int line = file_line(fp);
    printf("line = %d\n", line);
    fclose(fp);
    return 0;
}

第2题

2.用fgets和fputs实现文件的拷贝,将a.c的内容拷贝到b.c中;
提示:在没有读到文件末尾(返回值是否为NULL)的情况下,读一行写一行
#include <stdio.h>
void file_copy(FILE* rp, FILE* wp)
{
    char str[10] = "";
    //在while中已经拿出判断了,所以只需要写即可,不用再拿出来
    while(fgets(str, 10-1, rp) != NULL)
    {
        fputs(str, wp);
        bzero(str, sizeof(str));
    }
}
int main(int argc, const char *argv[])
{
    if(argc < 2)
    {
        return -1;
    }
    FILE* fp_r = fopen(argv[1], "r");
    if(NULL == fp_r)
    {
        perror("fopen");
        return -1;
    }
    FILE* fp_w = fopen("./b.c", "w");
    file_copy(fp_r, fp_w);
    fclose(fp_r)fclose(fp_w);               
    return 0;
}
11)fwrite
功能:将数据以二进制的格式输出到文件中;
头文件:
       #include <stdio.h>
原型:
       size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
参数:
    void *ptr:要输出的数据的地址;可以是任意类型数据;
	size_t size:以size个字节为单位输出;例如
        		如果要输出的是int类型数据,size == 4;
				如果要输出的是char类型数据,size == 1;
				如果要输出的是自定义结构体 struct aa,size == sizeof(struct aa);
	size_t nmemb:要输出的数据个数;
	FILE *stream:流指针;
返回值:
    成功,返回输出的数据个数,其实就是nmemb;
	失败,返回-1,更新errno;
例子
    FILE* fp = fopen("./fwrite.txt", "w");
	if(NULL == fp)
	{
		perror("fopen");
		return -1;
	}
	//fwrite写入数据
	int arr[4] = {1,2,3,4};
	if(fwrite(arr, 4, 4, fp)<0)
	{
		perror("fwrite");
		return -1;
	}
	printf("写入成功\n");
	fclose(fp);
12)fread
功能:从文件中获取二进制数据;
头文件:
       #include <stdio.h>
原型:
        size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
参数:
    void *ptr:成功读取数据后,数据存储的位置;
	size_t size:以size个字节为单位输入;例如
        		如果要输入的是int类型数据,size == 4;
				如果要输入的是char类型数据,size == 1;
				如果要输入的是自定义结构体 struct aa,size == sizeof(struct aa);
	size_t nmemb:要输入的数据个数;
	FILE *stream:流指针;
返回值:
    成功,返回读取到的个数nmemb;
	失败,返回-1,更新errno;

注意
1.比如要读的是文本文件,则可以指定按字符读写,如果是数据文件,则可以指定以整型数为对象读写
2.成功返回实际读写的个数,出错时返回EOF,如果指定大小大于缓冲区大小,不会报错,会将缓冲区的个数读出并返回
3.读到文件末尾时返回0

例子
fp = fopen("./fwrite.txt", "r");
    int brr[4] = {0};
    fread(brr, 4, 4, fp);
    int i =0;
    for(i=0; i<4; i++)
    {
        printf("%d ",brr[i]); 	//打印 1 2 3 4
    }
    putchar(10);
    fclose(fp);
    return 0;
}
13)fseek
功能:偏移文件指针;
头文件:
       #include <stdio.h>
原型:
       int fseek(FILE *stream, long offset, int whence);
参数:
    FILE *stream:流指针;
	long offset:相对于whence的偏移量
    int whence:
		SEEK_SET 	文件开头  
		SEEK_CUR 	当前位置
       	SEEK_END	文件结尾
返回值:
    成功,返回0;
	失败,返回-1,更新errno;
    void rewind(FILE *stream); 将文件指针偏移到开头; 
    rewind(fp) 相当于 fseek(fp, 0, SEEK_SET);

注意
1.fseek和lseek不可以用于无名管道
2.获取文件大小方法A:用fseek和ftell

    fseek(fp, 0, SEEK_END);
    long size = ftell(fp);

获取文件大小方法B:用lseek

	int c=lseek(fp);
14)ftell
功能:获取文件指针当前位置到文件开头的偏移量;
头文件:
       #include <stdio.h>
原型:      
       long ftell(FILE *stream);
参数:
    FILE *stream:流指针;
返回值:
    成功,返回非负数;
	失败,返回-1;
15)time
功能:获取从1970-01-01到至今的秒数;
头文件:
       #include <time.h>
原型:
       time_t time(time_t *t);
参数:
    time_t *t:存储获取到的时间; 	time_t : long int
返回值:
    成功,返回获取到的时间;
    失败,返回(time_t) -1,更新errno;
    //方法1
    time_t t = time(NULL);
    //方法2                                                             
    time_t t1; 
    time(&t1);
16)localtime
功能:将1970-01-01到至今的秒数,转换成日历格式;
头文件:
       #include <time.h>
原型:
    struct tm *localtime(const time_t *timep);
参数:
    time_t *timep:需要转换的秒数的地址;
返回值:
    成功,返回存储日历的结构体地址。struct tm *
struct tm
{
  int tm_sec;           /* Seconds. [0-60] (1 leap second) */ 	秒
  int tm_min;           /* Minutes. [0-59] */      分                  
  int tm_hour;          /* Hours.   [0-23] */		时
  int tm_mday;          /* Day.     [1-31] */		日
  int tm_mon;           /* Month.   [0-11] */		月 = tm_mon+1
  int tm_year;          /* Year - 1900.  */			年 = tm_year+1900
  int tm_wday;          /* Day of week. [0-6] */	星期
  int tm_yday;          /* Days in year.[0-365] */
  int tm_isdst;         /* DST.     [-1/0/1]*/
}
    失败,返回NULL,更新errno;
例子
#include <stdio.h>
#include <time.h>
int main(int argc, const char *argv[])
{
    //方法1 
    while(1)
    {
        time_t t = time(NULL);
        //方法2
        time_t t1;
        time(&t1);
    //  printf("%ld %ld\n", t, t1);
        struct tm* info = localtime(&t);
        if(NULL == info)
        {
    		perror("localtime");   
            return -1;
        }
     // \r为在控制台同一行覆盖刷新输出
        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;
} 
小练习
1.获取当前的时间,以日历的形式输出到time.txt中。要求如下:
​    1) 格式:[行号] 时间
​           [0] 星期3 2020-12-21 16-58-06
​           [1] 星期3 2020-12-21 16-58-07
​		   [2] 星期3 2020-12-21 16-59-00
2)程序结束后,再次运行程序,保证编号从上次的编号开始
#include <stdio.h>                                                                          
#include <time.h>
//获取文件行数
int file_line(FILE* fp)
{
    int line = 0;
    int c = 0;
    while((c = fgetc(fp)) != EOF)
    {
        if('\n' == c)
        {
            line++;
        }
    }
    return line;
}
int main(int argc, const char *argv[])
{
    FILE* fp = fopen("./time.txt", "a+");
    if(NULL == fp)
    {
        perror("fopen");
        return -1;
    }
    int line = file_line(fp);
    //循环获取时间
    time_t t = 0;
    struct tm* info = NULL;
    while(1)
    {
        line++;
        t = 0;
        info = NULL;
        t = time(NULL);
        info = localtime(&t);
        if(NULL == info)
        {
            perror("localtime");
            return -1;
        }
        fprintf(fp,"[%02d] 星期%d %d-%02d-%2d %02d-%02d-%02d\n",line, info->tm_wday, info->tm_year+1900, info->tm_mon+1, info->tm_mday,info->tm_hour, info->tm_min, info->tm_sec);
        fflush(fp);
        sleep(1);
    }
    fclose(fp);
    return 0;
}
17)sprintf
功能:将数据格式化输出到字符串中;可做数据拼接。
头文件:
    #include <stdio.h>
原型:
	int sprintf(char *str, const char *format, ...);
参数:
    char *str:将数据输出到该字符串中;
	char *format:格式化输出;
	...:不定参数;
返回值:
    成功,返回格式化输出的数据个数;
	失败,返回负数;
例子
int main(int argc, const char *argv[])
{
	int a = 10;
	char str[10] = "hello"; 	//"hello10"
	char str1[10] = "";
	char buf[15] = "";
	printf("%s%04d\n", str, a);
	sprintf(buf, "%s %04d",str, a);
	printf("%s\n",buf);
	int b = 0;
	sscanf(buf, "%s %d",str1, &b);
	printf("str1=%s, b=%d\n", str1, b);
	return 0;
}
18)sscanf
功能:从字符串中以标准格式输入数据
头文件:
    #include <stdio.h>
原型:
	int sscanf(const char *str, const char *format, ...);
参数:
    const char *str:从这个字符串中获取数据
   	const char *format :格式化输入
    ...:格式化输出
返回值:
    成功,返回成功赋值的变量个数。
    	为0,成功赋值的个数为0;
    失败,返回-1,更新errno
例子
#include <stdio.h>
    int main(int argc, const char *argv[])
    {
        char str[20] = "hello world 123";
        char s1[10] = "";
        char s2[10] = "";
        int a = 0;
        sscanf(str, "%s %s %d", s1, s2, &a);
        printf("%s %s %d\n",s1, s2, a);      
        return 0;
    }
sscanf的格式化:
    %c 	: 获取一个字符
    %*c : 抑制一个字符的接收。
    %[^\n]: 获取内容,直到\n为止,不包括'\n'
    %[^,]:  获取内容,直到遇到‘,’为止,不包括','
    %*[^\n]:抑制字符的吸收,直到遇到\n为止。
    %*[^,]: 抑制字符的吸收,直到遇到','为止,','依然存在;
例子
int main(int argc, const char *argv[])
{
    //hello,world
    char s1[20]="", s2[10]="";
    sscanf("hello,world","%[^,]%*c%s",s1,s2);   
    printf("%s %s\n",s1, s2); 	//hello world
    return 0;
} 

3. 文件IO

1. 文件IO是不带缓冲区的
2. 文件IO函数是由操作系统提供的,与操作系统绑定,又称之为系统调用
3. 文件IO是通过文件描述符来维护一个文件。

【1】文件描述符

1.概念

1. 尝试打开一个文件的时候,系统会主动给这个文件分配一个编号(文件描述符),用这个编号来描述一个文件。
2. 标准IO是对文件IO的二次封装,在文件IO的基础上封装了一个缓冲区,同时将文件描述符也一起封装到了FILE结构体中。
3. 文件IO对文件的读写,是通过文件描述符实现的。
4. 标准IO对文件的读写,是通过操作流指针实现的。

2.特殊的文件描述符

特殊的流指针文件描述符
stdinstdin->_fileno0STDIN_FILENO
stdoutstdout->_fileno1
stderrstderr->_fileno2

3.文件描述符的总量

文件描述符的编号:[0,1023] ,总共1024个。
其中0,1,2分别对应stdin, stdout, stderr.
注意

1.每个进程能打开的最大文件描述符1023,数量是1024
​2.文件描述符的资源是有限的,在不使用的情况下,需要关闭;

思考:如何验证文件描述符的总量,以及最大值。
提示:循环打开文件,不关闭

#include <stdio.h>
int main(int argc, const char *argv[])
{
 	printf("%d %d %d\n", stdin->_fileno, stdout->_fileno, stderr
    FILE* fp = NULL;
    while(1)
    {   
        fp = fopen("fileno.txt", "w");
        if(NULL == fp) 
        {   
            perror("fopen");
            break;
        }   
        printf("%d ", fp->_fileno);
    }        
    putchar(10);                                    
    return 0;
}
getdtablesize()函数
功能:获取当前进程能打开的文件描述符总量;
头文件:
       #include <unistd.h>
原型:
       int getdtablesize(void);
返回值:
    返回当前进程能打开的文件描述符总量;
例子:
    int size = getdtablesize();
    printf("size = %d\n", size); 	//1024

【2】文件IO函数

1.常见的文件IO函数

man 2 卷

open / close  打开关闭一个文件
read / write  读写一个文件
lseek 	偏移

2.文件IO函数的使用

1)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_WRONLY|O_CREAT|O_TRUNC
 			O_RDONLY:只读
         	O_WRONLY:只写
         	O_RDWR:读写
         ----以上三个必须包含一种----------
			O_APPEND:追加的方式打开
         	O_CREAT:文件不存在则创建文件
			O_TRUNC:清空文件;
	mode_t mode:文件的八进制权限,0777 0664;
	如果 int flags包含了 O_CREAT ;必须要有mode参数,否则会忽略 O_CREAT
              S_IRWXU  00700  
              S_IRUSR  00400 user has read permission
              S_IWUSR  00200 user has write permission
              S_IXUSR  00100 user has execute permission
		--------------以上是当前用户的权限--------------------
              S_IRWXG  00070 
              S_IRGRP  00040 group has read permission
              S_IWGRP  00020 group has write permission
              S_IXGRP  00010 group has execute permission

              S_IRWXO  00007 
              S_IROTH  00004 others have read permission
              S_IWOTH  00002 others have write permission
              S_IXOTH  00001 others have execute permission
                    
         0777  = S_IRWXU|S_IRWXG|S_IRWXO;
返回值:
    成功,返回文件描述符;
	失败,返回-1,更新errno;
int creat(const char *pathname, mode_t mode); //创建文件;
creat("./text.txt", 0777); 
相当于 open("./text.txt", O_RDWR|O_TRUNC|O_CREAT, 0777);
2)umask

The permissions of the created file are (mode & ~umask):文件创建的真实权限是 mode & ~umask;

1.什么是umask

umask:文件权限掩码,会影响文件创建时候的权限。文件创建的真实权限是 mode & ~umask;

2.如何查看umask

$ umask 得到结果 0002;
​实际创建的文件权限:
0777 & ~0002 = 111 111 111 & ~000 000 010 = 111 111 111 & 111 111 101 = 111 111 101 = 0775

3.设置umask
方法1:在终端上设置,但是只在当前终端有效
​$ umask 0000 		//将umask清零
方法2:通过umask()函数设置	
头文件:
       #include <sys/types.h>
       #include <sys/stat.h>
原型:
       mode_t umask(mode_t mask);
例子:
    umask(0);
例子
umask(0);
	int fd = open("./open.c", O_WRONLY|O_CREAT|O_TRUNC, 0777);
	if(-1 == fd)
	{
		perror("open");
		return -1;
	}
	printf("打开成功\n");
	close(fd);
3)close
功能:关闭一个文件。
头文件:
       #include <unistd.h>
原型:
       int close(int fd);
参数:
    int fd:要关闭的文件描述符;
返回值:
    成功,返回0;
	失败,返回-1;更新errno

思考:标准IO中的 r r+ w w+ a a+ 分别对应文件IO中的O_XXX的那些组合

fopenopen
rO_RDONLY
r+O_RDWR
wO_WRONLY|O_CREAT|O_TRUNC
w+O_RDWR|O_CREAT|O_TRUNC
aO_WRONLY|O_CREAT|O_APPEND
a+O_RDWR|O_CREAT|O_APPEND
4)write
功能:将数据写入到文件中;
头文件:
       #include <unistd.h>
原型
    ssize_t write(int fd, const void *buf, size_t count);
参数:
    int fd:文件描述符;
	void *buf:要写入文件中的数据首地址,可以是任意类型;
	size_t count:要写入的数据大小,以字节为单位;
返回值:
    成功,返回写入的数据个数;
	失败,返回-1,更新errno;
例子
int main(int argc, const char *argv[])
{
	umask(0); 	//清空umask
	int fd = open("write.c", O_RDWR|O_CREAT|O_TRUNC, 0777);
	if(-1 == fd)
	{
		perror("open");
		return -1;
	}
	char str[20] = "hello world";
	int res = write(fd, str, strlen(str));
	if(res < 0)
	{
		perror("write");
		close(fd);
		return -1;
	}
	printf("成功写入%d个字符\n",res);
	int arr[3] = {1,2,3};
	res = write(fd, arr, sizeof(arr));
	if(res < 0)
	{
		perror("write");
		close(fd);
		return -1;
	}
	printf("成功写入%d个字符\n",res);
	//偏移文件指针
	int size = lseek(fd, 0, SEEK_SET);
	bzero(str, sizeof(str));
	bzero(arr, sizeof(arr));
	res = read(fd,str, 11);
	if(res < 0)
	{
		perror("read");
		close(fd);
		return -1;
	}
	printf("%d %s\n", res, str);
	res = read(fd, arr, 12);
	printf("%d %d %d %d\n",res, arr[0],arr[1], arr[2])//将文件指针偏移到结尾
	size = lseek(fd, 0, SEEK_END);
	printf("size=%d\n",size);
	close(fd);
	return 0;
}
5)read
功能:从文件中读取数据;
头文件:
       #include <unistd.h>
原型:
       ssize_t read(int fd, void *buf, size_t count);
参数:
    int fd:文件描述符;
	void *buf:存储读取到的数据;
	size_t count:要读取的数据大小,以字节为单位;  
返回值:
    成功,返回读取到的数据个数;
	返回0,表示读到文件结尾。
	失败,返回-1,更新errno;

注意
1.返回0,表示读到文件结尾

6)lseek
功能:偏移文件指针;
头文件:
       #include <sys/types.h>
       #include <unistd.h>
原型:
       off_t lseek(int fd, off_t offset, int whence);
参数:
    int fd:文件描述符;
	off_t offset:相对于int whence的偏移量;
	int whence:
		SEEK_SET 文件开头
        SEEK_CUR 当前位置;
        SEEK_END 文件结尾;
返回值:
    成功,返回设置后的文件指针相对于文件开头的偏移量;
	失败,返回-1,更新errno;
计算文件大小:
    size = lseek(fd, 0, SEEK_END);
    printf("size=%d\n",size);  
	相当于
    fseek(fp, 0, SEEK_END);
    long size = ftell(fp);  
小练习:

1.通过write read函数实现图片的拷贝;

#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
int main(int argc, const char *argv[])
{
    int fd_r = -1, fd_w = -1;
    if((fd_r = open("./1.png", O_RDONLY))<0)
    {
        fprintf(stderr, "line:%d", __LINE__);
        perror("open");
        return -1;
    }
    if((fd_w = open("./2.png", O_WRONLY|O_CREAT|O_TRUNC, 0664))<0)
    {
        fprintf(stderr, "line:%d", __LINE__);
        perror("open");
        return-1;  
     }
    char c = 0;
    while(1)
    {
        int ret = read(fd_r, &c, 1);
        if(ret < 0)
        {
            perror("read");
            break;
        }
        else if(!ret)
        {
            printf("拷贝结束\n");
            break;
        }
        ret = write(fd_w, &c, 1);
        if(ret < 0)
        {
            perror("write");
            close(fd_r);
            close(fd_w);
            return -1;
        }
    }
    close(fd_r);
    close(fd_w);
    return 0;
}

3.文件属性相关的函数

1)stat
功能:获取文件属性;
头文件:
       #include <sys/types.h>
       #include <sys/stat.h>
       #include <unistd.h>
原型:
       int stat(const char *path, struct stat *buf);
参数:
    char *path:指定文件的路径+名字;
	struct stat *buf:系统定义的结构体,存储获取到的文件属性;
           struct stat {
               dev_t     st_dev;     /* ID of device containing file */
               ino_t     st_ino;     /* inode number */ 				inode号
               mode_t    st_mode;    /* protection */					文件类型+权限
               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
               dev_t     st_rdev;    /* device ID (if special file) */
               off_t     st_size;    /* total size, in bytes */ 		文件大小
               blksize_t st_blksize; /* blocksize for filesystem I/O */
               blkcnt_t  st_blocks;  /* number of 512B blocks allocated */
               time_t    st_atime;   /* time of last access */ 			最后一次被访问的时间
               time_t    st_mtime;   /* time of last modification */ 	最后一次被修改的时间
               time_t    st_ctime;   /* time of last status change */ 	最后一次被改变状态的时间
           };
返回值:
    成功,返回0;
	失败,返回-1,更新errno;
例子
    struct stat buf;
    int ret = stat("./1_fileno.c", &buf);
    if(ret < 0)
    {  
        perror("stat");
        return -1; 
    }   
    printf("获取属性成功\n");
    //inode号
	printf("inode:%ld\n", buf.st_ino);
	//文件类型+权限
	printf("mode: %o\n", buf.st_mode);
	//硬链接数
	printf("link: %d\n", buf.st_nlink);
2)提取文件权限
mode_t    st_mode;    /* protection */					文件类型+权限
st_mode:是一个32位无符号的整形变量;其中低9位([0, 8]),代表了文件的权限(rwx rwx rwx)
用st_mode & 相应的权限八进制数,如果结果大于0,说明有该权限,如果结果为0,则没有该权限。
st_mode: 100664
    664---> 110 110 100
    & 		100 000 000 	0400
    --------------------
    		100 000 000 -->0400 > 0 说明当前用户有可读权限
    
     664---> 110 110 100
      & 	 001 000 000  	0100
    --------------------
    		 000 000 000 == 0 当前用户没有可执行权限

对应权限的宏:

           S_IRWXU    00700     mask for file owner permissions
           S_IRUSR    00400     owner has read permission
           S_IWUSR    00200     owner has write permission
           S_IXUSR    00100     owner has execute permission
           S_IRWXG    00070     mask for group permissions
           S_IRGRP    00040     group has read permission
           S_IWGRP    00020     group has write permission
           S_IXGRP    00010     group has execute permission
           S_IRWXO    00007     mask for permissions for others (not in group)
           S_IROTH    00004     others have read permission
           S_IWOTH    00002     others have write permission
           S_IXOTH    00001     others have execute permission
例子
    //文件权限
	getFileType(buf.st_mode);
//获取文件权限方法1
#if 0
void getPermission(mode_t mode)
{
	if(mode & 0400)
		putchar('r');
	else
		putchar('-');
	if(mode & 0200)
		putchar('w');
	else
		putchar('-');
	if(mode & 0100)
		putchar('x');
	else
		putchar('-');
	if(mode & 0040)
		putchar('r');
	else
		putchar('-');
	if(mode & 0020)
		putchar('w');
	else
		putchar('-');
	if(mode & 0010)
		putchar('x');
	else
		putchar('-');
	if(mode & 0004)
		putchar('r');
	else
		putchar('-');
	if(mode & 0002)
		putchar('w');
	else
		putchar('-');
	if(mode & 0001)
		putchar('x');
	else
		putchar('-');
	putchar(10);
}
#endif
//获取文件权限方法二:
void getPermission(mode_t mode)
{
	int i =0;
	for(i=8; i>=0; i--)
	{
		if((mode & (1<<i)) == 0) //000 000 001 << 8 = 100 000 000
		{
			putchar('-');
			continue;
		}
		switch(i%3)
		{
		case 2:
			putchar('r');
			break;
		case 1:
			putchar('w');
			break;
		case 0:
			putchar('x');
			break;
		}
	}
	putchar(10);
}
3)提取文件类型

文件类型:7种 bsp-lcd

i)通过宏函数判断
           S_ISREG(m)  is it a regular file? 		判断是否是普通文件
           S_ISDIR(m)  directory? 					判断是否是目录文件
           S_ISCHR(m)  character device?			c
           S_ISBLK(m)  block device? 				b
           S_ISFIFO(m) FIFO (named pipe)? 			p
           S_ISLNK(m)  symbolic link?  (Not in POSIX.1-1996.) 	l
           S_ISSOCK(m) socket?  (Not in POSIX.1-1996.) 		s
参数:
     m:mode_t st_mode;  
返回值:
    1,代表是该类型文件;
	0,代表不是该类型文件;
例子
//获取文件类型  方法1:
void getFileType(mode_t m)
{
    if(S_ISREG(m))
        putchar('-');
    else if(S_ISDIR(m))
        putchar('d');
    else if(S_ISCHR(m))
        putchar('c');
    else if(S_ISBLK(m))
        putchar('b');
    else if(S_ISFIFO(m))
        putchar('p');
    else if(S_ISLNK(m))
        putchar('l');
    else if(S_ISSOCK(m))
        putchar('s');
}
ii)通过提取文件类型
mode_t    st_mode;    /* protection */					文件类型+权限
st_mode:其中[低16位, 低13位]共4bit, 代表文件类型;
S_IFMT     0170000   bit mask for the file type bit fields
​					001 111
st_mode & S_IFMT :实现提取[低16位, 低13位]共4bit,这4bit就代表了文件类型。
st_mode: 100664
    	 100664 --> 001 000 000 110 110 100
S_IFMT   170000 --> 001 111 000 000 000 000
    ---------------------------------------------
    				001 000 ... ... ... ...  提取出4bit: 001 000;
	将结果与下列的宏做比较,如果相等,那么就是对应的文件类型:
           S_IFSOCK   0140000   socket 				s
           S_IFLNK    0120000   symbolic link 		l
           S_IFREG    0100000   regular file 		-
           S_IFBLK    0060000   block device 		b
           S_IFDIR    0040000   directory 			d
           S_IFCHR    0020000   character device 	c
           S_IFIFO    0010000   FIFO				p
例子
//获取文件类型 方法2:
void getFileType(mode_t m)
{
    mode_t type = m & S_IFMT;
    switch(type)
    {
    case S_IFSOCK:
        putchar('s');
        break;
    case S_IFLNK:
        putchar('l');
        break;
    case S_IFREG:
        putchar('-');
        break;
    case S_IFBLK:
        putchar('b');
        break;
    case S_IFDIR:
        putchar('d');
        break;
    case S_IFCHR:
        putchar('c');
        break;
    case S_IFIFO:
        putchar('p');
        break;
    }
}
4)获取文件所属用户
uid_t     st_uid;     /* user ID of owner */ 			当前用户的uid
getpwuid
功能:通过 struct stat 结构体中的 uid_t st_uid 成员变量,获取文件所述用户;
头文件:
       #include <sys/types.h>
       #include <pwd.h>
原型:
       struct passwd *getpwuid(uid_t uid);
参数:
    uid_t uid:文件所属用户的uid;
返回值:
    成功,返回存储文件所属用户信息的结构体;
	失败,NULL,更新errno;
           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 */
           };   
例子
//文件所属用户名;
	char usr_name[20] = "";
	getUsrName(usr_name , buf.st_uid);
	printf("%s\n", usr_name);
//获取文件所属用户的名字
char* getUsrName(char* usr_name, uid_t uid)
{
	struct passwd *pwd = NULL;
	pwd = getpwuid(uid);
	if(NULL == pwd)
	{
		perror("getpwuid");
		return NULL;
	}
	strcpy(usr_name, pwd->pw_name);
	return usr_name;
}
5)获取文件所属用户组
 gid_t     st_gid;     /* group ID of owner */ 			组的gid
getgrgid
功能:通过 struct stat 结构体中的  gid_t   st_gid 成员变量,获取文件所述用户组;
头文件:
       #include <sys/types.h>
       #include <grp.h>
原型:
       struct group *getgrgid(gid_t gid);
参数:
    gid_t gid:
返回值:
    成功,返回存储组用户信息的结构体指针  struct group *
    失败,返回NULL;更新errno;
           struct group {
               char   *gr_name;       /* group name */
               char   *gr_passwd;     /* group password */
               gid_t   gr_gid;        /* group ID */
               char  **gr_mem;        /* group members */
           };
例子
//获取文件所属组用户的名字
char* getGrpName(char* grp_name, gid_t gid)
{
	struct group *grp = getgrgid(gid);
	if(NULL == grp)
	{
		perror("getgtgid");
		return NULL;
	}
	strcpy(grp_name, grp->gr_name);
	return grp_name;
}
//文件所属组用户的名字;
	char grp_name[20] = "";
	getGrpName(grp_name, buf.st_gid);
	printf("%s\n", grp_name);
小练习

读取一个文件的信息,main函数外部传参的方式。要求输出结果格式如下;
​ -rw-rw-r-- 1 linux linux 377 10 22 10:46 1_fileno.c

4.操作目录的函数

1)opendir
功能:根据目录里名打开一个目录;
头文件:
       #include <sys/types.h>
       #include <dirent.h>
原型:
       DIR *opendir(const char *name);
参数:
    char *name:指定要打开的目录的路径+名字;
返回值:
    成功,返回 DIR * 目录流指针;
	失败,返回NULL;
例子
int main(int argc, const char *argv[])
{
	DIR* dp = opendir("./");
	if(NULL == dp)
	{
		perror("opendir");
		return -1;
	}
	printf("打开目录成功\n");
	closedir(dp);
	return 0;
}
2)closedir
功能:关闭目录;
头文件:
SYNOPSIS
       #include <sys/types.h>
       #include <dirent.h>
原型:
       int closedir(DIR *dirp);
参数:
    DIR *dirp:目录流指针;
返回值:
    成功,返回0;
	失败,返回-1;
3)readdir
功能:通过DIR*目录流指针,读取目录;
头文件:
       #include <dirent.h>
原型:
       struct dirent *readdir(DIR *dirp);
参数:
    DIR *dirp:目录流指针;
返回值:
    成功,返回存储目录信息的结构体地址, struct dirent *;
    失败,返回NULL;更新errno;
           struct dirent {
               ino_t          d_ino;       /* inode number */
               off_t          d_off;       /* not an offset; see NOTES */
               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]; /* filename */ 	目录中文件名字
           };

注意
1.读到文件末尾返回NULL

例子
int main(int argc, const char *argv[])
{
	DIR* dp = opendir("./");
	if(NULL == dp)
	{
		perror("opendir");
		return -1;
	}
	printf("打开目录成功\n");
	//读取目录中的数据
	struct dirent *dir_read = NULL;
	while(1)
	{
		dir_read = readdir(dp);
		if(NULL == dir_read)
		{
			perror("readdir");
			fprintf(stderr, "目录已读取完毕\n");
			break;
		}
		printf("%s\n", dir_read->d_name);
	}
	closedir(dp);
	return 0;
}
小练习
1.代码实现,打开目录,显示该目录下面所有文件的属性,权限,最有一次被修改的时间,类似ls –l。例子:
​    -rw-rw-r-- 1 linux linux    377  10 22 10:46 1_fileno.c
​	drwxrwxr-x 5 linux linux 4096 10 21 01:29:47 2020 . 
​	drwxrwxr-x 2 linux linux 4096 9 20 23:25:06 2020 file1 
​	-rw-rw-r-- 1 linux linux 	21 	8 21 01:29:47 2020 t1.c 
​	drwxrwxr-x 2 linux linux 4096 7 20 23:25:06 2020 file3 
​	drwxrwxr-x 2 linux linux 4096 4 20 23:25:06 2020 file2
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <pwd.h>
#include <string.h>
#include <grp.h>
#include <time.h>
#include <dirent.h>
//获取文件权限
void getPermission(mode_t mode)
{
    int i =0;
    for(i=8; i>=0; i--)
   {  
        if((mode & (1<<i)) == 0) //000 000 001 << 8 = 100 000 000
        {
            putchar('-');
            continue;
        }
        switch(i%3)
        {
        case 2:
            putchar('r');
            break;
        case 1:
            putchar('w');
            break;
        case 0:
            putchar('x');
            break;
        }
    }
}
//获取文件类型
void getFileType(mode_t m)
{
    mode_t type = m & S_IFMT;
    switch(type)
    {
    case S_IFSOCK:
        putchar('s');
        break;
    case S_IFLNK:
        putchar('l');
        break;
    case S_IFREG:
        putchar('-');
        break;
    case S_IFBLK:
        putchar('b');
        break;
    case S_IFDIR:
        putchar('d');
        break;
    case S_IFCHR:
        putchar('c');
        break;
    case S_IFIFO:
        putchar('p');
        break;
    }
}
//获取文件所属用户的名字
char* getUsrName(char* usr_name, uid_t uid)
{
    struct passwd *pwd = NULL;
    pwd = getpwuid(uid);
    if(NULL == pwd)
    {
        perror("getpwuid");
        return NULL;
    }
    strcpy(usr_name, pwd->pw_name);
    return usr_name;
}
//获取文件所属组用户的名字
char* getGrpName(char* grp_name, gid_t gid)
{
    struct group *grp = getgrgid(gid);
    if(NULL == grp)
    {
        perror("getgtgid");
        return NULL;
    }
    strcpy(grp_name, grp->gr_name);
    return grp_name;
}
//获取时间
char* getTime(char* time, time_t t)
{
    struct tm* info = localtime(&t);
    sprintf(time,"%02d %2d %02d:%02d", info->tm_mon+1, info->tm_mday,info->tm_hour, info->tm_min);
    return time;
}
int main(int argc, const char *argv[])
{
    if(argc < 2)
    {
        fprintf(stderr, "请输入文件路径+文件名\n");
        return -1;
    }
    DIR* dp = opendir(argv[1]);
    if(NULL == dp)
    {
        perror("opendir");
        return -1;
    }
    struct dirent* rp = NULL;
    struct stat buf;
    char path[30] = "";
    int size = 0;
    char time[30] = "";
    while(1)
    {
        rp = readdir(dp);
        if(NULL == rp)
        {
            break;
        }
        bzero(path, sizeof(path));
        sprintf(path, "%s%s", argv[1], rp->d_name);
        //  printf("path = %s\n", path);
        int ret = stat(path, &buf);
        if(ret < 0)
        {
            perror("stat");
            return -1;
        }
        //文件类型+权限
        getFileType(buf.st_mode);
        getPermission(buf.st_mode);
        //硬链接数
        printf(" %2d", buf.st_nlink);
        //文件所属用户名;
        char usr_name[20] = "";
        getUsrName(usr_name , buf.st_uid);
        printf("%7s", usr_name);
        //文件所属组用户的名字;
        char grp_name[20] = "";
        getGrpName(grp_name, buf.st_gid);
        printf("%7s", grp_name);
        //文件大小
        size = buf.st_size;
        printf("%7d",size);
        //时间
        bzero(time, strlen(time));
        printf("%14s", getTime(time, buf.st_mtime));
        //名字
        printf("%10s\n", rp->d_name);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值