文件IO(标准C库IO、Linux IO、文件缓存、目录操作函数 )-day12

目录

前言

一、标准C库I/O

1.1fopen、fclose函数

1.2fgetc、fputc、fgets、 fputs、fread、fwrite函数

1.3fseek、ftell

1.4综合练习

二、Linux I/O

2.1open、read、write、close

2.2perror、lseek函数

2.3stdin、stdout、stderr

2.4标准C库IO与linux文件IO比较

三、文件缓存

3.1无缓存、行缓存、全缓存

四、目录文件函数

4.1stat函数

4.2opendir、readdir、closedir

总结



前言

文件IO主要包括标准C库提供的函数、和Linux系统自带的文件IO函数,了解文件缓存的细节及不同函数的区别;掌握目录文件的相关操作函数


提示:以下是本篇文章正文内容,下面案例可供参考

一、标准C库I/O

文件 I/O(Input/Output)类型:
	(1)标准C IO 
			#include <stdio.h>
			标准C库为我们提供一堆库函数,可以对文件操作(打开文件,读取文件内容,写入文件内容,关闭文件)
	(2)Linux 文件IO 
			系统调用
			Linux系统自带的一堆函数,可以对文件操作(打开文件,读取文件内容,写入文件内容,关闭文件)

1.1fopen、fclose函数

1、fopen函数 (f file的简写)
(1)功能:打开文件
(2)头文件及函数原型
	#include <stdio.h>
	FILE *fopen(const char *path, const char *mode);
	FILE* fp = fopen("hello.c","r"); //打开文件,以相对路径 只读 的方式打开hello.c
	FILE* fp = fopen("/home/linux/0703/IO/hello.c","r");//打开文件,以绝对路径的方式打开
	
(3)参数说明
	const char *path //打开文件的名字(可以是相对路径也可以是绝对路径) 
	const char *mode //打开文件的方式
	
	r ---> read 	w ---> write 	a ---> append 追加
	"r"  以只读的方式打开文件	"r+" 以可读可写的方式打开文件

	"w"  以只写的方式打开文件,如果文件存在,清空并打开,如果不存在,创建并打开
	"w+" 以可读可写的方式打开文件,如果文件存在,清空并打开,如果不存在,创建并打开
	
	"a"  以只写的方式打开文件,如果文件存在,打开,接着尾巴写,如果不存在,创建并打开
	"a+" 以可读可写的方式打开文件,如果文件存在,打开,接着尾巴写,如果不存在,创建并打开
	
(4)返回值	
	FILE * 指针,文件流指针,FILE是一个结构体,每打开一个文件,内核就会开辟出一块空间,用来保存这个文件得到相关信息
	成功:返回 FILE *(非空), FILE* fp  流指针fp,指向的就是被打开的文件
	失败:返回 NULL
(5) 实例
	FILE *fp = fopen("hello.c","r"); //打开文件,以相对路径的方式打开
	FILE *fp = fopen("/home/linux/0703/IO/hello.c","r");//打开文件,以绝对路径的方式打开
**********************************案例代码***************************************
#include <stdio.h>
int main(int argc, const char *argv[])
{
	//1.相对路径的方式打开当前目录下的hello.c
	FILE* fp = fopen("hello.c","r");
	if(fp == NULL)
	{
		printf("打开失败!!\n");
	}
	else
	{
		printf("打开成功!!\n");
	}
	return 0;
}
**********************************************************************************
2、fclose函数
(1)功能:关闭文件,同时会刷新缓存区,将缓存区的内容清空
(2)头文件及函数原型
 #include <stdio.h>
 int fclose(FILE *fp);
 
(3)参数说明:FILE *fp //fopen的返回值
	
(4)返回值 
成功: 返回 0
失败: 返回 -1

1.2fgetc、fputc、fgets、 fputs、fread、fwrite函数

fgetc、fputc函数

3、fgetc函数  file  get  char //咬文嚼字读

(1)功能:每次读取一个字符
(2)头文件及函数原型
 #include <stdio.h>  
 int fgetc(FILE* stream);//stream 文件流
 
(3)参数说明 
 FILE *stream  //fopen函数的返回值

(4)返回值  
	成功:实际读取的字符
		
	失败:返回 EOF(-1)  ,当读取到文件的尾巴的时候,返回EOF  -1
		#define EOF -1//宏定义
(5)实例 
	char ch; //char可以理解为特殊的整数
	FILE *fp = fopen("hello.c","r");
	ch = fgetc(fp);
/代码演示/
#include <stdio.h>

int main(int argc, const char *argv[])
{
	char ch;//用来保存读取出的字符
	//1.相对路径的方式打开当前目录下的hello.c
	FILE* fp = fopen("./01-test.c","r");
	if(fp == NULL)
	{
		printf("打开失败!!\n");
	}
	else//打开成功
	{
		printf("打开成功!!\n");
#if 0
		ch = fgetc(fp);//调用一次,读取出一个字符,保存到ch中
		printf("%c",ch);//打印
		ch = fgetc(fp);//继续向后读取一个字符,保存到ch中
		printf("%c",ch);//打印
		ch = fgetc(fp);//继续向后读取一个字符,保存到ch中
		printf("%c",ch);//打印
#endif
		//思考:如何将01-test.c中的全部内容读取出来
		//循环读取,那么什么时候读取到尾巴???,让循环结束,当fgetc函数的返回值是-1的时候
		while(1)
		{
			ch = fgetc(fp);//调用一次,读取出一个字符,保存到ch中
			//每读取一次,判断一下是否读取到文件的尾巴,如果读取到文件的尾巴,循环结束
			if(ch == EOF)//if(ch == -1)
			{
				break;//结束循环
			}
			printf("%c",ch);//打印
		}
		//读写文件的前提条件是 文件必须是打开的,而且有对应的读写权限	
		fclose(fp);//关闭文件
	}
	return 0;
}
************************************************************************************
4、fputc函数 //fputc写入一个字符 咬文嚼字写

(1) 功能:向文件中写入一个字符
(2) 头文件及函数原型 
	#include <stdio.h>
    int fputc(int c, FILE *stream);
(3) 参数说明:
	int c //将要被写入文件的字符
	FILE *stream //fopen返回值,写入文件流指针指向的那个文件  流指针代表的就是打开的那个文件	
(4)返回值:
	成功:
		返回写入的字符
	失败:
		返回 -1 
(5)实例
	char ch = 'A'; //将要被写入文件的字符
	fputc(ch,fp);
//案例代码
#include <stdio.h>

int main(int argc, const char *argv[])
{
	//因为要写入一个字符,所以用的是"w"
	FILE* fp = fopen("./xixi.txt","w"); //如果文件不存在,有创建功能
	if(fp == NULL)
	{
		printf("fopen failed!!\n");
		return -1;//提前结束程序
	}
	
	//程序如果能够执行到这,说明没有执行return -1, 打开文件成功
	char ch = '#';//ch中保存的是即将写入文件的字符
	fputc(ch,fp);//调用一次,写入一个字符
	ch = 'A';
	fputc(ch,fp);//再调用一次,继续向后写入一个字符
	ch = 'B';
	fputc(ch,fp);//再调用一次,继续向后写入一个字符
	//关闭文件
	fclose(fp);
	return 0;
}
************************************************************************************
练习:用命令行传参./a.out a.c b.c,实现内容拷贝
#include <stdio.h>

int main(int argc, const char *argv[])
{
	//容错判断
	if(argc != 3)
	{
		printf("忘记传递参数了!! ./a.out a.c b.c \n");
		return -1;
	}
	char ch;//用来保存读取出的字符
	//以读的方式打开一个文件,以写的方式打开另一个文件
	FILE* fpr = fopen(argv[1],"r");
	FILE* fpw = fopen(argv[2],"w");
	if(fpr == NULL || fpw == NULL)
	{
		printf("fopen failed!!\n");
		return -1;
	}
	//循环拷贝,每读取一个字符,就立刻写入另一个文件
	while(1)
	{
		ch = fgetc(fpr); //读取一个字符保存到ch中
		if(ch == EOF)//读取到文件的尾巴
			break;
		fputc(ch, fpw); //将刚读取出的字符ch,写入到文件中
	}
	fclose(fpr);
	fclose(fpw);
	return 0;
}

fgets、 fputs、fflush函数

**********************************************************************************
5、fgets函数

(1)功能:每次读取一行字符串
(2)头文件及函数原型
	#include <stdio.h>
	char *fgets(char *s, int size, FILE *stream);
(3)参数说明: 
	char buf[100]; //用来保存读取出来的字符串 
	fgets(buf, 100, fp);
	
	char *s  //读取的数据存放的位置    
	int size  //一次最多读取100个字符
	FILE *stream //fopen函数的返回值 
(4)返回值:
	成功:char * 返回值指向读取出字符串的指针
	
	失败:返回 NULL  ,读取到文件尾返回NULL 
(5) 实例
	char buf[100]; //用来保存读取的一行
	fgets(buf, 100, fp);
//案例代码
#include <stdio.h>

int main(int argc, const char *argv[])
{
	char buf[100] = { 0 };//用来保存读取出的一行字符串
	FILE* fp = fopen("./02-test.c","r");
	if(fp == NULL)
	{
		perror("fopen failed");
		return -1;
	}
#if 0
	fgets(buf, 100, fp);//调用一次fgets函数,就读取一行,存储到buf中
	printf("%s",buf);//将buf打印
	fgets(buf, 100, fp);//调用一次fgets函数,就继续向后读取一行,存储到buf中
	printf("%s",buf);//将buf打印
	fgets(buf, 100, fp);//调用一次fgets函数,就继续向后读取一行,存储到buf中
	printf("%s",buf);//将buf打印
#endif 
	char* ret = NULL;//用来保存fgets的返回值,来判断是否读取到文件尾巴
	//方法一
	/*
	while(1)
	{
		ret = fgets(buf, 100, fp);
		if(ret == NULL)//读取到文件尾巴
		{
			break;
		}
		printf("%s",buf);
	}
	*/
	//方法二 
	while(fgets(buf, 100, fp) != NULL)//fgets函数返回值 == NULL 循环结束
	{
		printf("%s",buf);
	}
	fclose(fp);
	return 0;
}
**********************************************************************************
6、fputs函数 
(1)功能:每次写入一行 		
(2)头文件及函数原型
	#include <stdio.h>
    int fputs(const char *s, FILE *stream);

(3)参数说明: 
	char buf[100] = "hello world!!!";
	fputs(buf, fp);//将buf数组中存储的字符串,写入到fp指向的打开的那个文件中
	const char *s  //即将写入文件的字符串存放的位置
	FILE *stream //fopen函数的返回值
(4)返回值:
	成功: >= 0
	失败:返回 EOF  
(5)实例:
	char buf[100] = "hello world";
	fputs(buf,fp);
//案例代码
#include <stdio.h>
int main(int argc, const char *argv[])
{
	char buf[100] = "hello world";//即将写入文件中的字符串
	int i;
	FILE* fp = fopen("haha.txt","w");
	if(fp == NULL)
	{
		perror("fopen failed");
		return -1;
	}
	//将一个字符串写入到haha.txt中  循环输入3个名字,写入到haha.txt中
	for(i = 0; i < 3; i++)
	{
		printf("请输入要写入的名字:\n");
		scanf("%s",buf);//你输入两次之后,ctrl+c,程序直接结束,没有刷新缓存区,所以前两次的名字没有写入成功
		fputs(buf, fp); 
	}
	fclose(fp);//关闭文件,同时刷新缓存区
	return 0;
}
//上面的程序,输入两次名字后,按ctrl + c,前两次的名字没有写入成功,证明标准IO存在缓存区
//flush函数,刷新缓存区
#include <stdio.h>
#include <string.h>

int main(int argc, const char *argv[])
{
	char buf[100] = "hello world";//即将写入文件中的字符串
	int i;
	FILE* fp = fopen("haha.txt","w");
	if(fp == NULL)
	{
		perror("fopen failed");
		return -1;
	}
	//将一个字符串写入到haha.txt中
	//循环输入3个名字,写入到haha.txt中
	for(i = 0; i < 3; i++)
	{
		printf("请输入要写入的名字:\n");
		scanf("%s",buf);//你输入两次之后,ctrl+c,程序直接结束,没有刷新缓存区,所以前两次的名字没有写入成功
		//要将写入的名字换行
		strcat(buf,"\n");//在输入的字符串尾巴后,加上一个\n
		fputs(buf, fp);
		//我们如何实现,每写入一个名字,就立刻写入haha.txt,人为刷新缓存区,fflush函数
		fflush(fp);//刷新缓存区
	}
	fclose(fp);//关闭文件,同时刷新缓存区
	return 0;
}

---------------------------------练习 复制文件------------------------------------
上一个练习是用 fgetc 和 fputc实现文件拷贝
用fgets和fputs,命令行传参 实现文件的拷贝
//./a.out a.c b.c

#include <stdio.h>
int main(int argc, const char *argv[])
{
	//容错判断
	if(argc != 3)
	{
		printf("忘记传递参数了!! ./a.out a.c b.c \n");
		return -1;
	}
	char buf[100] = { 0 };//用来保存读取出的每行字符串
	FILE* fpr = fopen(argv[1],"r");
	FILE* fpw = fopen(argv[2],"w");
	if(fpr == NULL || fpw == NULL)
	{
		perror("fopen failed");
		return -1;
	}
	//循环读取拷贝
	//方法一
	/*
	while(1)
	{
		//思想:循环,读取一行,就立刻写入一行
		char* ret = fgets(buf, 100, fpr);
		if(ret == NULL)//每读取一次,对返回值进行一次判断,是否读取到文的尾巴
		{
			break;
		}
		fputs(buf,fpw);
	}
	*/
	//方法二
	while(fgets(buf, 100, fpr) != NULL)//读取一行,立刻写入一行,读取到文件尾巴,循环结束
	{
		fputs(buf,fpw);
	}
	//关闭文件 
	fclose(fpr);
	fclose(fpw);
	return 0;
}

fread、fwrite、memset函数

**********************************************************************************
fgetc fputc //一次读写一个字符  咬文嚼字 
fgets fputs //一次读写一行字符串 一次一行
fread fwrite//一次读取写入一大块   (fread 就不会受到 \n的影响)
7、fread函数 
(1)功能: 按块读  每次读取一段(注意一段以块数为单位)
(2)头文件及函数原型 
	#include <stdio.h>
    size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
	有类型的指针,可以赋值给无类型的指针
(3)参数说明: 
	void *ptr  //读取出的数据存放的位置
	size_t size  //一块的大小(一块占几个字节)
	size_t nmemb //一次最多读取多少块
	FILE *stream  //fopen函数的返回值
	
	char buf[100] = { 0 };
	buf ---> char*
	fread(buf, 1, 100, fp);//一块的大小是1个字节,一次最多读取100块
	fread(buf, 10, 10, fp);//一块的大小是10个字节,一次最多读取10块

	int b[10];	
	b ---> int*
	fread(b, 4, 10, fp); //一块的大小是4个字节,一次最多读取10块
(4)返回值:
	成功:实际读取的 块数  (当前不行,不能将块数改为字节数)
	失败: 小于0
-------------------例子1次读1000字节------------------------
#include <stdio.h>
int main(int argc, const char *argv[])
{
	char buf[1000] = { 0 };//用来保存读取到的内容
	FILE* fp = fopen("./01-test.c","r");
	if(fp == NULL)
	{
		perror("fopen failed");
		return -1;
	}
	//fread一次,就将 01-test.c的全部内容读取出来了,是buf数组足够大 
	//01-test.c文件中的字符 < 1000个
	int ret = fread(buf, 1, 1000, fp);//1块的大小是1个字节,一次最多读取1000块
	printf("实际读取的块数是%d\n",ret);
	printf("%s",buf);
	return 0;
}
--------------------例子分批次读-------------------------------
//假设你的buf不1000,不足够装下01-test.c文件的全部内容,我们需要用循环读取
#include <stdio.h>
#include <string.h>

int main(int argc, const char *argv[])
{
	char buf[100] = { 0 };//用来保存读取到的内容
	FILE* fp = fopen("./01-test.c","r");
	if(fp == NULL)
	{
		perror("fopen failed");
		return -1;
	}
	//用于buf数组,不足够大,一次不能装下01-test.c全部内容,所以需要循环读取
	while(fread(buf, 1, 100, fp) > 0)//只要fread函数返回值 > 0 ,就说明实际读取到了内容
	{
		printf("%s",buf);
		//为了解决还有残留物的bug,我们需要每次读取之后,清空一次buf数组,要不然buf数组中,有可能存有上一次读取的残留物
		memset(buf, 0, sizeof(buf));//将buf数组的整个清空,全部赋值为'\0'
	}
	fclose(fp);
	return 0;
}
******************************************************************************
8、fwrite
(1)功能:按块写 (可以以二进制的方式写入)
(2)头文件及函数原型
	#include <stdio.h>
	//void* 无类型指针,可以被其他的有类型的指针赋值
	size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
(3)参数说明: 
	char buf[100] = "hello world!!";
	fwrite(buf,1, 100, fp);
	
	const void *ptr  //即将写入文件数据存放的位置, 需要的是指针,存储位置的首地址
	size_t size  //实际写入过程中,1块的大小
	size_t nmemb  //实际要写入的块数
	FILE *stream  //fopen函数的返回值
(4)返回值:
	成功:实际写入的块数
	失败:小于0
对比:	fwrite(buf, 1, 100,	fpw) 和 fwrite(buf, 1, strlen(buf), fpw)区别

#include <stdio.h>
#include <string.h>

int main(int argc, const char *argv[])
{
	char buf[100] = "hello world!!";
	FILE* fpw = fopen("./haha.c","w");
	if(fpw == NULL)
	{
		perror("fopen failed");
		return -1;
	}
//	fwrite(buf, 1, 100,	fpw);//写成100,代表100块,将整个buf数组的全部内容,100块都写进去了
	fwrite(buf, 1, strlen(buf), fpw);//只将buf数组的前13块,写入文件中	
	fclose(fpw);
	return 0;
}

文件分为两种类型
	(1) 可以用记事本打开,并且不是乱码的,是可见字符,这种文件叫文本文件, 里面保存的是ascii码	
	   hello.c 	   a.txt
	(2) 二进制文件,可执行文件,如果用记事本打开,是乱码
int a[10] = {11,22,33,44,55,66,77,88,99,100};
int b[10] = { 0 };

//1. 先将a数组中的内容写入到一个文件 a.txt中 
fwrite(a, 4, 10, fp); //一块的大小是4个字节,一次写入10块
//2. 将a.txt中的内容读取出来,写入到b数组中
fread(b, 4, 10, fp);//一块的大小是4个字节,最多读取10块

#include <stdio.h>
#include <string.h>

int main(int argc, const char *argv[])
{
	int i;
	int a[10] = {11,22,33,44,55,66,77,88,99,100};
	int b[10] = { 0 };
	//将a数组中的内容写入到文件a.txt中
	FILE* fp = fopen("./a.txt","w");
	if(fp == NULL)
	{
		perror("fopen failed");
		return -1;
	}
	fwrite(a, 4, 10, fp); //一块的大小是4个字节,一次写入10块
	//写完之后,文件指针,在文件的尾巴
	//关闭文件
	fclose(fp);

	fp = fopen("./a.txt","r");//关闭文件之后,再次打开文件,文件指针默认在文件的头
	if(fp == NULL)
	{
		perror("fopen failed");
		return -1;
	}
	//将a.txt中的整数,读取到b数组中
	fread(b, 4, 10, fp); //一块的大小是4个字节,一次最多读取10块
	fclose(fp);
	
	for(i = 0; i < 10; i++)
	{
		printf("%d\n",b[i]);
	}

	return 0;
}

printf  sprintf  fprintf 函数

///printf  sprintf  fprintf 函数///
int fprintf(FILE *stream, const char *format, ...);
int printf(const char *format, ...);
int sprintf(char *str, const char *format, ...);

#include <stdio.h>

int main(int argc, const char *argv[])
{
	char buf[100] = { 0 };
	int a = 10;
    //printf("a is %d\n",a);//将"a is 10"这个字符串,打印输出到屏幕上
	sprintf(buf,"a的值是%d\n",a);//将"a的值是10"这个字符串打印输出到buf数组中,相当于给buf数组赋值	
	//sprintf已经将原本打印输出到屏幕中字符串,打印输出到buf数组中
	//把buf数组打印
	printf("buf is %s",buf);
	//fprintf 
	FILE* fp = fopen("xixi.c","w");
	//条件判断,暂时不写
	
	fprintf(fp,"a ------> %d\n",a);
	//将"a ------> 10"这个字符串打印输出到fp流指针,指向的打开文件,就相当于将"a-->10"这个字符串,
	//写入xixi.c文件
	return 0;
}

1.3fseek、ftell

9、fseek()函数
(1)功能:移动文件指针的位置 
(2)头文件及函数原型 
	 #include <stdio.h>
     int fseek(FILE *stream, long offset, int whence);
(3)参数说明:
	FILE *stream //fopen函数的返回值
	long offset  //偏移量  正数向后移动,负数向前移动
	int whence   //相对的基准位置
	
	SEEK_SET //相对于文件的首 
	SEEK_END //相对于文件的尾巴
	SEEK_CUR //相对于当前文件指针的位置 current 

	fseek(fp,100,SEEK_SET); //相对于文件的首,向后移动100个位置	
	fseek(fp,-100,SEEK_END); //相对于文件的尾,向前移动100个位置
	fseek(fp,100,SEEK_CUR); ///相对于当前文件指针的位置,向后移动100个位置
	fseek(fp,-100,SEEK_CUR); ///相对于当前文件指针的位置,向前移动100个位置
	fseek(fp, 0, SEEK_SET); //没有偏移量,相当于将文件指针,移动到文件的首 
	fseek(fp, 0, SEEK_END); //没有偏移量,相当于将文件指针,移动到文件的尾巴
	
(4)返回值:
	成功: 0
	失败: EOF
	
#include <stdio.h>
int main(int argc, const char *argv[])
{
	FILE* fp = fopen("test.c","w+");//不存在(创建) + 存在(清空) + 读写
	if(fp == NULL)
	{
		perror("fopen failed");
		return -1;
	}
	fputc('A',fp);
	fputc('B',fp);
	//因为写入文件之后,文件指针在文件的尾巴,要想从头读取,需要将文件指针
	//移动到文件的首部
	//fseek(fp, 0, SEEK_SET);//没有偏移量,相当于移动的文件的首部
	//fseek(fp, -2, SEEK_END);//相对于文件的尾巴,向前移动2个位置
	//写入A和B之后,当前的文件指针,在文件的尾巴
	fseek(fp, -2, SEEK_CUR);//相对于当前文件指针的位置,向前移动2个
	char ch = fgetc(fp);
	printf("ch is %d\n",ch);
	printf("%c",ch);

	fclose(fp);
	return 0;
}
***********************************************************************************
10、ftell()函数
	
(1)功能:获取文件指针的位置 
(2)头文件及函数原型
	 #include <stdio.h>
     long ftell(FILE *stream);

(3)参数说明:
	FILE *stream //fopen函数的返回值	
(4)返回值
   成功:返回当前文件指针的位置 
   失败:EOF
   
#include <stdio.h>
int main(int argc, const char *argv[])
{
	long post;//保存文件指针的位置
	FILE* fp = fopen("test.c","w+");//不存在(创建) + 存在(清空) + 读写
	if(fp == NULL)
	{
		perror("fopen failed");
		return -1;
	}
	//刚打开文件,获取下文件指针的位置
	post = ftell(fp);
	printf("post is %ld\n",post);//post is 0
	fputc('A',fp);//写入字符'A'后,文件指针,向后移动一个位置
	//写入一个字符后,再次获取文件指针的位置
	post = ftell(fp);
	printf("post is %ld\n",post);//post is 1 
	fputc('B',fp);
	post = ftell(fp);
	printf("post is %ld\n",post);//post is 2 
	//因为写入文件之后,文件指针在文件的尾巴,要想从头读取,需要将文件指针
	//移动到文件的首部
	//fseek(fp, 0, SEEK_SET);//没有偏移量,相当于移动的文件的首部
	//fseek(fp, -2, SEEK_END);//相对于文件的尾巴,向前移动2个位置
	//写入A和B之后,当前的文件指针,在文件的尾巴
	fseek(fp, -2, SEEK_CUR);//相对于当前文件指针的位置,向前移动两个
	char ch = fgetc(fp);
	printf("ch is %d\n",ch);
	printf("%c",ch);
	post = ftell(fp);
	printf("post is %ld\n",post);//post is 1
	fclose(fp);
	return 0;
}

1.4综合练习

练习:统计文件行数
#include "my.h"

int main(int argc, const char *argv[])
{
	char ch;
	char buf[100] = { 0 };
	int count = 0;//用来统计行数
	FILE* fp = fopen("./01-test.c","r");
	if(fp == NULL)
	{
		perror("fopen failed");
		return -1;
	}
	//方法一 fgetc实现
#if 0
	while((ch=fgetc(fp)) != EOF)
	{
		if(ch == '\n')
			count++;
	}
	printf("row is %d\n",count);
#endif
	while(fgets(buf, 100, fp) != NULL)
	{
		count++;
	}
	printf("row is %d\n",count);
	fclose(fp);
	return 0;
}
练习:用fread和fwrite实现文件拷贝  ./mycp aaa bbb    // ./mycp a.c b.c
#include "my.h"
int main(int argc, const char *argv[])
{
	if(argc != 3)
	{
		printf("忘记传递参数了!! ./a.out a.c b.c");
		return -1;
	}
	int ret;
	char buf[100]={0};
	FILE* fp=fopen(argv[1],"r");
	FILE* fv=fopen(argv[2],"w");
	if(fp == NULL || fv == NULL)
	{
		perror("fopen failed");
		return -1;
	}
	//每次实际读取多少块,就写入多少块
	//fread函数的返回值就是每次实际读取的块数
	while((ret=fread(buf,1,100,fp))>0)
		fwrite(buf,1,ret-1,fv);//参数是ret,代表实际写入的块数,实际读取多少块,就写入多少
	fclose(fp);
	fclose(fv);
	return 0;
}
****************************************综合测试***********************************
编程读写一个文件test.txt,每隔1秒向文件中写入一行数据,类似这样
//test.txt 不存在创建,追尾写内容
./a.out 
1,  2007-7-30 15:16:42
2,  2007-7-30 15:16:43
ctrl + c
该程序应该无限循环,直到按Ctrl-C中断程序。下次再启动程序写文件时可以追加到原文件之后,并且序号能够接续上次的序号,比如:
./a.out ,接着上一次的尾巴,接着写,你需要统计行号,fgets,一次读取一行,统计文件中的原有行数
1,  2007-7-30 15:16:42
2,  2007-7-30 15:16:43
3,  2007-7-30 15:19:02
4,  2007-7-30 15:19:03
5,  2007-7-30 15:19:04
***************************************代码****************************************
#include <time.h>
#include <stdio.h>

struct tm{
    int tm_sec;         /* seconds */
    int tm_min;         /* minutes */
    int tm_hour;        /* hours */
    int tm_mday;        /* day of the month */
    int tm_mon;         /* month */
    int tm_year;        /* year */
    int tm_wday;        /* day of the week */
    int tm_yday;        /* day in the year */
    int tm_isdst;       /* daylight saving time */
};
int main(int argc, const char *argv[])
{

	//1.获取距今的秒数
	time_t raw_time;//用来保存秒数
	struct tm* tp = NULL;//用来保存指向中文时间结构体的指针
	int line = 0;//用来记录行号
	char buf[100] = { 0 };//用来保存读取到的一行内容
	//2.先打开test.txt,统计test.txt中的已经存在内容的行数,方便追尾写
	FILE* fp = fopen("test.txt","a+");//注意此处是a+ 
	if(fp == NULL)
	{
		perror("fopen failed");
		return -1;
	}
	while(fgets(buf, 100, fp) != NULL)
		line++;
	printf("line is %d\n",line);//line is 5
	while(1)
	{
		time(&raw_time);//要写在循环的里面,调用一次,得到一次距今的秒数,再转换为年 月 日 
		//将秒数转为中文格式的时间
		tp = localtime(&raw_time);
		//打印
	//	printf("%d,%02d-%02d-%02d %02d:%02d:%02d\n",++line,tp->tm_year+1900,tp->tm_mon+1,tp->tm_mday,
	//		tp->tm_hour,tp->tm_min,tp->tm_sec);
	
		fprintf(fp,"%d,%02d-%02d-%02d %02d:%02d:%02d\n",++line,tp->tm_year+1900,tp->tm_mon+1,tp->tm_mday,
			tp->tm_hour,tp->tm_min,tp->tm_sec);
		
		/*
		//将格式化好的字符串,存储到buf数组中,然后再将buf数组写入文件
		sprintf(buf,"%d,%02d-%02d-%02d %02d:%02d:%02d\n",++line,tp->tm_year+1900,tp->tm_mon+1,tp->tm_mday,
			tp->tm_hour,tp->tm_min,tp->tm_sec);	
		fputs(buf, fp);
		*/
		//因为标准IO存在缓存区,所以每写一次,需要刷新缓存区
		fflush(fp);//刷新缓存区
		sleep(1);
	}
	return 0;
}

二、Linux I/O

2.1open、read、write、close

##############################################################################
linux 文件IO
	 POSIX 可移植性操作系统规范 规定的一些列对文件操作的函数(打开、读写、关闭)
	 没有缓存
	打开 读 写 关闭 Linux系统自带的,是系统调用
##############################################################################
1、open函数
(1)功能:打开文件文件 
(2)头文件及函数原型
	#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);
(3)参数说明: 
	const char *pathname  //打开文件的名称,可以用相对路径也可以用绝对路径
	int flags //打开文件的方式 (读 写 清空 追尾写)
	
	O_RDONLY //只读 read 
	O_WRONLY //只写 write 
	O_RDWR   //读 + 写
	O_CREAT  //创建 
	O_TRUNC  //清空 
	O_APPEND //追加尾巴
	
	mode_t mode //文件的权限 0666 ,当第二个参数中有O_CERAT的时候,需要加第三个参数,创建文件的权限
(4)返回值:
	成功 :返回值是int类型,文件描述符(对应的就是open函数打开的那个文件的标识)
		int fd  //返回值 ,如果成功是非负的正整数
	失败:-1
(3)案例 
	int fd = open("hello.c",O_RDONLY);//以只读的方式,打开当前下的hello.c文件
	int fd = open("hello.c",O_RDONLY | O_CREAT,0666); //如果文件不存在,创建并打开
------------------------------------例子代码----------------------------------------
///my.h
#ifndef _MY_H
#define _MY_H  //如何避免头文件的重复包含
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#endif


#include "my.h"
//在预处理阶段,会将my.h中的内容全部拷贝过来

int main(int argc, const char *argv[])
{
	//打开文件
	int fd = open("./haha.c",O_RDWR | O_CREAT | O_TRUNC, 0666);//以可读可写的方式打开,如果文件存在,清空并打开,不存在,创建并打开
	if(fd == -1)
	{
		perror("打开失败原因");
		return -1;
	}
	printf("打开成功!!!\n");
	printf("fd is %d\n",fd); //fd is 3 ,为什么是从3开始的呢???后面讲

	//关闭文件
	close(fd);
	return 0;
}
***********************************************************************************
2、close(int fd);   
	功能: 关闭文件
	参数:open函数的返回值
  返回值:
    成功:0 失败:-1
************************************************************************************
3、read函数
(1) 功能:
	从文件中读取数据(按字节)
(2) 头文件及函数原型 
	 #include <unistd.h>
     ssize_t read(int fd, void *buf, size_t count);
(3) 参数说明:
	int fd  //open函数的返回值 
	void *buf //读出数据存放的位置
	size_t count  //最多读取的字节数

	char buf[100] = { 0 };//用来保存读取到的内容	
	int ret = read(fd, buf, 100);//ret 代表read实际读取到的字节数
	printf("%s",buf);
(4) 返回值:
	成功:只要函数的返回值 > 0 ,就说明读取到了内容   实际读取到的字节数 
	失败:-1
(5)实例 
	char buf[100] = { 0 }; 
	read(fd,buf,sizeof(buf));
------------------------------------------------------------------------------------
---------------------------------分批次读取实例 -------------------------------------
#include "my.h"
//在预处理阶段,会将my.h中的内容全部拷贝过来

int main(int argc, const char *argv[])
{
	char buf[100] = { 0 };//用来保存读取到的文件内容
	//打开文件
	int fd = open("./haha.c",O_RDWR);//以可读可写的方式打开
	if(fd == -1)
	{
		perror("打开失败原因");
		return -1;
	}
	printf("打开成功!!!\n");
	printf("fd is %d\n",fd);

	//写一个while循环,将文件的内容全部读取出来
	while(read(fd, buf, sizeof(buf)) > 0)//只要函数的返回值>0,就说明读取到了内容
	{
		printf("%s",buf);
		memset(buf, 0, sizeof(buf));//最后一次读取,实际读取的字节数可能 != 100,之后覆盖前面的一部分,后面的
		//是上一次的残留物,所以每次读取之前,都清空buf数组,避免有残留物的bug
	}
	//关闭文件
	close(fd);
	return 0;
}

------------------------------------------------------------------------------------
------------------------------------------------------------------------------------
char buf[100];
fread(buf, 1, sizeof(buf), fp) 返回值是实际读取到的块数,对象数,记录数 ,不能说字节数
int b[10];//sizeof(b) == 40

fread(b, 4, 10, fp);  1块的大小是4个字节, 一次最多读取10块
read(fd, buf, sizeof(buf));  返回值 实际读取到的字节数
------------------------------------------------------------------------------------
------------------------------------------------------------------------------------
练习:实现如果文件不存在,创建,文件存在,输出文件内容  文件名从参数中传递过来
	./a.out a.c
#include "my.h" //在预处理阶段,会将my.h中的内容全部拷贝过来

int main(int argc, const char *argv[])
{
	if(argc != 2)
	{
		printf("忘记传递参数了!! ./a.out a.c\n");
		return -1;
	}

	char buf[100] = { 0 };//用来保存读取到的文件内容
	//打开文件
	int fd = open(argv[1],O_RDONLY | O_CREAT, 0666);//如果文件存在,以只读方式打开,不存在,创建并以只读方式打开
	if(fd == -1)
	{
		perror("打开失败原因");
		return -1;
	}
	printf("打开成功!!!\n");
	printf("fd is %d\n",fd);
	//写一个while循环,将文件的内容全部读取出来
	while(read(fd, buf, sizeof(buf)) > 0)//只要函数的返回值>0,就说明读取到了内容
	{
		printf("%s",buf);
		memset(buf, 0, sizeof(buf));//最后一次读取,实际读取的字节数可能 != 100,之后覆盖前面的一部分,后面的
		//是上一次的残留物,所以每次读取之前,都清空buf数组,避免有残留物的bug
	}
	//关闭文件
	close(fd);
	return 0;
}
************************************************************************************
4、write函数
(1)功能:写入数据(按字节)
(2)头文件及函数原型 
	#include <unistd.h>
    ssize_t write(int fd, const void *buf, size_t count);
(3) 参数说明:
	int fd  //open函数的返回值 
	const void *buf  //即将写入文件的数据,存放的位置
	size_t count  //实际写入文件的字节数

	char buf[100] = "hello world!!";
	write(fd, buf, sizeof(buf));//将整个buf数组中的100个字节都写入文件
	write(fd, buf, strlen(buf)); //只将buf数组中的前 strlen(buf)个字符,写入文件	
(4) 返回值:
	成功:实际写入的字节数 
	失败: -1
(5)实例 
	char buf[100] = "hello"; 
	write(fd,buf,sizeof(buf)); 
	
//对比 write(fd, buf, sizeof(buf)) 和 write(fd, buf, strlen(buf)) 的区别
#include "my.h"

int main(int argc, const char *argv[])
{
	char buf[100] = "hello world!!";
	int fd = open("wuwu.txt",O_WRONLY|O_CREAT|O_TRUNC,0666);
	if(fd == -1)
	{
		perror("open failed");
		return -1;
	}
	//write(fd, buf, sizeof(buf));//将整个buf数组的100字节写入文件
	write(fd, buf, 13);//13可以改写为strlen(buf) 将整个buf数组的前13个字符,写入文件
	close(fd);
	return 0;
}

//案例代码
#include "my.h"

int main(int argc, const char *argv[])
{
	int i;
	char buf[100] = "hello world!!";
	int fd = open("wuwu.txt",O_WRONLY|O_CREAT|O_TRUNC,0666);
	if(fd == -1)
	{
		perror("open failed");
		return -1;
	}
	
	for(i = 0; i < 3; i++)
	{
		printf("请您输入要写入的名字:\n");
		scanf("%s",buf); //输入两次名字后,按ctrl + c,结束程序,前两次的名字,写入文件成功,证明 Linux文件IO无缓存区
		write(fd, buf, strlen(buf));//调用写入,立刻写入文件,Linux文件IO,因为没有缓存区
	}

	close(fd);
	return 0;
}
------------------------------------------------------------------------------------
结论:证明 Linux文件IO无缓存区
之前所学的标准IO是有缓存区的,如果提前按了ctrl + c,前两个名字是写入不成功的

2.2perror、lseek函数

5、perror函数(根据errno的值,输出错误信息)
	
	(1)功能:能够将程序执行失败的具体错误原因提示出来
	linux系统里有一个全局变量,errno用来保存错误信息编号,错误输出信息的打印根据错误编号而来	
	(2)头文集及函数原型
	 #include <stdio.h>
     void perror(const char *s);
	
	 perror("打开失败的原因是");
	(3) 参数说明:
		char *s //打印的错误提示
//案例代码
#include <stdio.h>

int main(int argc, const char *argv[])
{
	//1.相对路径的方式打开当前目录下的hello.cc
	FILE* fp = fopen("hello.cc","r");
	if(fp == NULL)
		//你知道打开失败的具体原因吗???
		//通过perror函数,可以打印输出 打开失败的具体原因,方便改错
		perror("打开失败的原因是");
	else
		printf("打开成功!!\n");
	return 0;
}	

//strerror函数,也能打印错误提示信息 		
#include <stdio.h>
#include <string.h>
#include <errno.h>

int main(int argc, const char *argv[])
{
	//1.相对路径的方式打开当前目录下的hello.cc
	FILE* fp = fopen("hello.cc","r");
	if(fp == NULL)
		//你知道打开失败的具体原因吗???
		perror("打开失败的原因是");
		printf("errno is %d\n",errno);//errno是一个全局变量
		printf("打开失败的原因是:%s\n",strerror(errno));
		//errno是个全局变量 记录的是错误信息的编号
		//strerror函数的返回值,就是那个错误原因字符串的首地址
	else
		printf("打开成功!!\n");
	return 0;
}
#######练习###### 求出某个文件大小
#include <stdio.h>
int main(int argc, const char *argv[])
{
	int count = 0;//统计字符的个数
	FILE* fp = fopen("./fseek.c","r");
	if(fp == NULL)
	{
		perror("fopen failed");
		return -1;
	}
	/*
	//方法一
	char ch;
	while(fgetc(fp) != EOF)//一次读取一个字符
	{
		count++;
	}
	printf("size is %d\n",count);
	*/
	//方法二
	//先将文件指针移动到文件的尾巴
	fseek(fp, 0, SEEK_END);
	//文件指针尾巴对应的下标位置,就是整个文件的大小
	printf("size is %ld\n",ftell(fp));
	return 0;
}
***********************************************************************************
6、lseek()函数
(1)功能:移动文件指针,同时能够获取文件指针的位置
	lseek功能 =  fseek功能 + ftell功能
(2)头文件及函数原型
	#include <sys/types.h>
	#include <unistd.h>
	
	typedef long off_t;
	off_t lseek(int fd, off_t offset, int whence);
	
(3)参数说明:	
	int fd  //open函数的返回值 
	off_t offset  //偏移量 正数先后移动 负数向前移动
	int whence  //相对于基准位置
	
	lseek(fd,100,SEEK_SET); //相对于文件的首,向后移动100个位置
	lseek(fd,-100,SEEK_END); //相对于文件的尾,向前移动100个位置
	lseek(fd,100,SEEK_CUR); ///相对于当前文件指针的位置,向后移动100个位置
	lseek(fd,-100,SEEK_CUR); ///相对于当前文件指针的位置,向前移动100个位置
	lseek(fd, 0, SEEK_SET); //没有偏移量,相当于将文件指针,移动到文件的首 
	lseek(fd, 0, SEEK_END); //没有偏移量,相当于将文件指针,移动到文件的尾巴
(4)返回值:
	成功:返回当前文件指针的位置 
	失败:-1

2.3stdin、stdout、stderr

标准输入、标准输出、标准错误输出
	FILE* fp = fopen();
	一个程序,在运行的时候,会自动打开三个 流 标准输入流、标准输出流、标准错误输出流
	./a.out 
	Linux系统中一切皆文件
	
	标准输入流  	stdin 		默认是键盘   文件描述符 0
	标准输出流 		stdout		默认是终端   文件描述符 1
	标准错误输出流 	stderr   	默认是终端   文件描述符 2
		error	stdin\stdout stderr
代码举例///
#include <stdio.h>
int main(int argc, const char *argv[])
{
	char buf[100] = { 0 };
	//1.键盘文件,从键盘输入中读取内容到buf数组中
	fgets(buf, 100, stdin);//stdin标准输入流,默认是键盘,效果类似于scanf("%s",buf)
//	printf("buf is %s",buf);
	//2.将buf数组中的内容,写入到终端,写入终端,就相当于在终端打印
	fputs(buf,stdout);//stdout 标准输出流,默认是终端
	fputs(buf,stderr);//stderr 标准错误输出流,默认是终端
	return 0;
}

2.4标准C库IO与linux文件IO比较

标准C库IO与linux文件IO比较:
标准I/O  fopen fclose  fgetc fputc fgets fputs fread fwrite fflush fprintf fseek ftell

	操作文件的时候,除了fopen函数以为,每个函数都要使用fopen函数的返回值,流指针 FILE* fp
	流指针,对应的是打开的那个文件

Linux文件I/O  open close read write lseek 

	操作文件的时候,除了open函数以为,每个函数都要使用open函数的返回值,文件描述符 int fd 
	文件描述符,对应的是打开的那个文件

(1) 标准I/O 有缓存区  Linux文件I/O没有缓存区	
(2) 函数返回值类型不同,标准I/O函数的返回值是 FILE* fp 流  Linux文件I/O返回值是int fd 文件描述符
(3) 标准I/O C库提供的函数, LInux文件I/O是 Linux系统自带的 系统调用

三、文件缓存

3.1无缓存、行缓存、全缓存

	(1)全缓存
#include <stdio.h>
int main(int argc, const char *argv[])
{
	char buf[100] = "hello world!!";//注意此处,没有换行符\n,等到缓存区满,一次性打印输出一堆
	while(1)
	{
		fputs(buf, stdout);//将buf数组的内容,写入到标准输出
		sleep(1);
	}
	return 0;
}
	(2)行缓存		
		行缓存:存在的前提条件是写入的文件是终端
#include <stdio.h>
int main(int argc, const char *argv[])
{
	char buf[100] = "hello world!!\n";//注意此处有\n,写入文件是终端,行缓存,直接打印输出
	while(1)
	{
		fputs(buf, stdout);//将buf数组的内容,写入到标准输出
		sleep(1);
	}
	return 0;
}
	(3)无缓存
	标准错误输出是无缓存
#include <stdio.h>

int main(int argc, const char *argv[])
{
	char buf[100] = "hello world!!";//注意此处没有有\n,
	while(1)
	{
		fputs(buf, stderr);//将buf数组的内容,写入到标准错误输出,没有缓存,立刻打印输出
		sleep(1);
	}
	return 0;
}	

四、目录文件函数

4.1stat函数

stat函数: 可以查看普通文件的属性,也可以查看目录文件(文件夹子)的属性
(1)功能:获取文件属性的
(2)头文件及函数原型
	#include <unistd.h>
	int stat(const char *path, struct stat *buf);
	struct stat s = { 0 }; //s是想用来保存文件属性相关信息的结构体
	//想要调用stat函数,实现修改结构体变量s的值,需要采用值传递还是地址传递????
	//要采用地址传递,因为地址传递可以修改实参变量的值
	//调用完stat函数之后,结构体变量s,不再是空,存上了文件的属性信息
	
	//s的类型是struct stat  &s的类型是 struct stat*
	
	stat("./hello.c",&s); //相对路径 
	stat("/home/linux/hello.c",&s); //绝对路径
一个函数想要给他的调用者传递值 1. return value; 返回值的形式  2. 参数上的地址传递 	

(3)参数说明: 
	const char *path  //获取文件属性的那个文件的名字,可以用相对路径也可以用绝对路径
	struct stat *buf 
(4)返回值:
	成功:返回: 0
	失败:返回: -1
struct stat {
        mode_t     st_mode;       //文件对应的模式,文件,目录等
        ino_t      st_ino;        //inode节点号
        dev_t      st_dev;        //设备号码
        dev_t      st_rdev;       //特殊设备号码
        nlink_t    st_nlink;      //文件的连接数
        uid_t      st_uid;        //文件所有者
        gid_t      st_gid;        //文件所有者对应的组
        off_t      st_size;       //普通文件,对应的文件字节数
        time_t     st_atime;      //文件最后被访问的时间
        time_t     st_mtime;      //文件内容最后被修改的时间
        time_t     st_ctime;      //文件状态改变时间
        blksize_t st_blksize;     //文件内容对应的块大小
        blkcnt_t   st_blocks;     //文件内容对应的块数量
};

*********************读出文件的最后修改时间和大小**********************
#include "my.h"

int main(int argc, const char *argv[])
{
	//定义一个文件属性结构体变量,用来保存文件的属性信息
	struct stat s = { 0 };
	int ret = stat("./01-test.c",&s);//参数上的地址传递的方式得到文件的属性
	if(ret == -1)
	{
		perror("stat failed");
		return -1;
	}
	printf("size is %ld\n",s.st_size);//文件大小 因为s是结构体变量,访问成员变量用.
	printf("last time is %s",ctime(&s.st_mtime));//文件最后的访问时间
	return 0;
}

4.2opendir、readdir、closedir

************************************************************************************
opendir()函数//打开的是目录文件

(1)功能:打开目录文件 directory
(2)头文件及函数原型
	#include <dirent.h>
	DIR *opendir(const char *name);
(3)参数说明:
	const char *name //打开的目录文件的名字,可以是相对路径也可以是绝对路径 
(4)返回值:
	成功:DIR*  //打开成功,返回值是 DIR*,指向这个打开的目录文件的指针 
	失败:NULL 
************************************************************************************
readdir()函数

(1)功能:读取目录文件,每次读取出一个文件 
(2)头文件及函数原型 
	#include <dirent.h>
	struct dirent *readdir(DIR *dirp);
(3) 参数说明:
	DIR *dirp //opendir 函数的返回值 
(4) 返回值:
	成功:struct dirent * //返回值是读取出的文件的信息结构体的首地址
		//既然我们有了结构体的首地址,就可以访问成员变量
	失败:返回 NULL //读取到目录文件尾巴的时候,返回NULL
	
#include <dirent.h>

struct dirent
{
   long d_ino; /* inode number 索引节点号 */
   off_t d_off; /* offset to this dirent 在目录文件中的偏移 */
   unsigned short d_reclen; /* length of this d_name 文件名长 */
   unsigned char d_type; /* the type of d_name 文件类型 */
   char d_name[NAME_MAX+1]; /* file name (null-terminated) 文件名,最长255字符 */
}
************************************************************************************
closedir()函数

(1)功能:关闭目录文件
(2)头文件及函数原型 
    #include <dirent.h>
    int closedir(DIR *dirp);
(4) 返回值:
    DIR *dirp //opendir函数的返回值
(4) 返回值:成功:0  失败:-1
    closedir(dp);
///
例子:打开目录,读出目录里面所有的文件(不包括隐藏文件)
/my.h///

#ifndef _MY_H
#define _MY_H
//如何避免头文件的重复包含

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <dirent.h>

#endif
	

#include "my.h"
int main()
{
	struct dirent* ds = NULL;
	//打开目录文件  
	DIR* dp = opendir("/home/linux/22021班"); //打开 22021班 这个目录文件
	if(dp == NULL)
	{
		perror("opendir failed");//打开失败
		return -1;
	}
	//读取目录文件中的文件,调用一次readdir函数,只能读取一个文件
	/*
	ds = readdir(dp);
	printf("filename is %s\n",ds->d_name);//调用一次readdir函数,读取一个文件
	ds = readdir(dp);
	printf("filename is %s\n",ds->d_name);//调用一次readdir函数,继续向后读取下一个文件
	ds = readdir(dp);
	printf("filename is %s\n",ds->d_name);//调用一次readdir函数,继续向后读取下一个文件
	ds = readdir(dp);
	printf("filename is %s\n",ds->d_name);//调用一次readdir函数,继续向后读取下一个文件
	*/
	
	//循环读取,将整个目录文件中的所有文件读取出来
	while((ds=readdir(dp)) != NULL)//当readdir返回值 == NULL,读取到目录尾巴,循环结束
	{
		//过滤一下,不显示隐藏文件
		if(ds->d_name[0] == '.')//以.开头的是隐藏文件
		{
			continue;//结束本次循环
		}
		printf("filename is %s\n",ds->d_name);
		//你有了文件的名字 
		//stat(路径+文件的名字);
	}
	//关闭目录文件 
	closedir(dp);
	return 0;
}		
-----------------------------------------------------------------------------------
练习:实现列出某个目录中所有文件属性(文件名,文件大小,文件最后修改时间)  stat()
	
	//获取属性,需要调用stat函数,使用stat函数,就需要获取文件属性文件的名字
	//读取目录文件 opendir  readdir closedir 
	目录名由命令行参数传入  ./a.out /home/linux
  文件名:%s 文件大小%ld 文件修改时间%s 

	
#include "my.h"

int main(int argc, char* argv[])
{
	if(argc != 2)
	{
		printf("忘记传递参数了!!\n ./a.out /home/linux");
		return -1;
	}
	int ret;
	char pathname[100] = { 0 };//用来保存路径+文件名字
	struct stat s = { 0 };//用来保存文件的属性
	struct dirent* ds = NULL;
	//打开目录文件  
	DIR* dp = opendir(argv[1]); 
	{
		perror("opendir failed");//打开失败
		return -1;
	}
	//循环读取,将整个目录文件中的所有文件读取出来
	while((ds=readdir(dp)) != NULL)//当readdir返回值 == NULL,读取到目录尾巴,循环结束
	{
		//过滤一下,不显示隐藏文件
		if(ds->d_name[0] == '.')//以.开头的是隐藏文件
		{
			continue;//结束本次循环
		}
		//printf("filename is %s\n",ds->d_name);
		sprintf(pathname,"%s/%s",argv[1],ds->d_name);
		//%s /home/linux  
		//%s haha.c 将路径和文件的名字拼接成一个完整的路径+文件名,保存到pathname中
		//获取每一个文件的属性
		ret = stat(pathname, &s);//直接获取属性,获取是当前目录下的文件名字,所以找不到文件
		if(ret == -1)
		{
			perror("stat failed");
			return -1;
		}
		printf("%s %ld %s\n",ds->d_name,s.st_size, ctime(&s.st_mtime));
	}
	//关闭目录文件 
	closedir(dp);
	return 0;
}
///关于sprintf函数的再次理解
sprintf可以个格式化出一个你想要的字符串,保存到一个数组中

#include <stdio.h>
int main(int argc, const char *argv[])
{
	char pathname[100];
	//sprintf可以个格式化出一个你想要的字符串,保存到一个数组中
	char *p = "/home/linux";
	char d_name[] = "haha.c";
     //printf("%s/%s",p,d_name);//打印输出到屏幕上
	//不是打印输出,而是合成一个新的字符串,存到pathname数组中
	sprintf(pathname,"%s/%s",p,d_name);//将原本打印输出到屏幕上的字符串,打印输出到pathname数组中,相当于给pathname数组赋值
	printf("pathname is %s\n",pathname);
	return 0;
}	
***********************************************************************************

 

总结

这里对文章进行总结:文件I/O主要包括标准C库提供的函数、和Linux系统自带的文件I/O函数,了解文件缓存的细节及不同函数的头文件、返回值、参数及意义区别;掌握目录文件的相关操作函数,记不住的情况下,可使用man命令,查看函数的详细信息!

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值