标准IO

							第一天
1.为什么需要用标准IO 
因为每个操作系统下面,对文件的管理和接口不一样的。!!!
	linux:open/read/write/close....
	windows:winopen,...
	
	同一个文件,在不同的操作系统下面,我们的操作文件的代码都不一样。
	
c语言标准委员会,就统一了文件操作的接口: 
	--》标准IO库,主要统一文件操作的接口
		“文件”:普通的文本文件和二进制文件 
			文本文件:无组织、无格式的文件,以字符的ASCII码等其它编码来解些的文件。
				如:.txt .c, .h, .s, .cpp, .java, ...
			二进制文件: 有特定格式的文件。
				如:.bmp, .jpg, .gif, .mp3, .mp4,...
				
在标准IO库,用结构体FILE结构体来描述或表示一个文件,然后在这个结构体中 
	创建两个缓冲区(一段内存), 一个读缓冲区,一个写缓冲区 
	
	FILE
	{
		char *in;		//指向读的缓冲区 
		char *out;		//指向写的缓冲区 
	};
				
	还提供了对“文件”操作的函数接口: 
		fopen/fclose/fread/fwrite/fseek/...
		puts/gets/fputs/fgets/scanf/printf ...
		
	使用标准IO的实现过程: 
		Your code --> 标准IO库(如:fopen/fclose..) -->系统IO----》内核---》Hardware
		
	FILE 有两个缓冲区(标准IO库中开辟的一段内存)	
		*in --> 读的缓冲区 
		*out--> 写的缓冲区
		
	标准IO带缓冲的IO,IO流,它的效率要比系统IO要高,why?
	because
		系统IO: 
			read 1byte  从硬盘中读一个字节出来
			
		标准IO: 
			fread 1byte,它会从硬盘上第一块(如:512bytes)出来,放到
			标准IO的读缓冲区。(内存条)
			
	标准IO: IO流 stream 跟水流一样,流走了就没有了。读走了就没有了 

	缓冲: 同步的问题。
		缓冲区的数据,何时同步到外设上去呢?
		缓冲区开多大呢? 
		
		标准IO缓冲区有三种类型: 行缓冲、全缓冲、无缓冲
		
		行缓冲:缓冲区数据达到一行,同步到外设上去。
					假设一行顶多80个字节。
					遇到\n(称换行符,一行结束的标志)
					就会把缓冲区中的数据同步到外设上去
		
		printf -->行缓冲
			main()
			{
				printf("wadadwadwadwadaw!");
				while(1);
			}
		全缓冲:缓冲区中数据要填满整个缓冲区,才同步到外设上去。
		
		
		
		无缓冲: 缓冲区中有一个字节,就同步到外设上去。
		
			perror --》 无缓冲 

标准IO库,会自动为每个进程,打开三个标准IO流(文件): 
		标准输入流:	FILE *stdin
			stdin是定义在<stdio.h>中的一个全局变量,它指向
			标准输入设备(一般为终端或键盘)
				scanf() <---- stdin  
				
		标准输出流:FILE *stdout
			stdout 是定义在<stdio.h>中的一个全局变量,它指向 
			标准输出设备(控制台,终端或屏幕)
			
				printf() <--- stdout 
				
		标准出错:FILE *stderr 
			 stderr是定义在<stdio.h>中的一个全局变量,它指向 
			 标准出错设备(一般为终端)
			 
				perror() <---stderr 

2.标准IO的函数接口 
(1)标准IO打开或关闭一个文件流 
	fopen 
	头文件
		#include <stdio.h>
	函数功能 
		用来打开一个普通文件(文本文件/二进制文件)
	函数原型
		FILE *fopen(const char *pathname, const char *mode);
	函数参数 
		const char *pathname		//要打开的那个文件的文件名
		const char *mode			//打开文件的方式,有如下几种: 
										"r":只读打开。文件不存在,则报错:
											打开后,光标在开头。
											
										"r+":读写打开,文件不存在,则报错; 
											打开后,光标在开头 
										"w":只写打开。文件不存在,则创建;
											打开后,文件内容截短(文件内容被清掉)
										"w+":读写打开,文件不存在,则创建; 
											打开后,文件内容截短(文件内容被清掉)
										"a":append 追加打开,文件不存在,则创建;
											打开后,光标在末尾。文件内容不会被截短
										"a+":读写打开。文件不存在,则创建。 
											原始读的位置在开头,原始写的位置在末尾
											
	函数返回值 
		成功:返回打开文件指针。FILE* 
				在标准IO中,FILE*代表一个打开的文件,后续
				标准IO库的函数都需要用到它
		失败:返回NULL,同时errno被设置。
		
	fclose:用来关闭stream指定的文件流	
	头文件
		#include <stdio.h>
	函数功能
		用来关闭stream指定的文件流 
	函数原型
		 int fclose(FILE *stream);
	函数参数
		FILE *stream		//要关闭的文件的FILE *
	函数返回值 
		 成功: 返回0 
		 失败: 返回-1,同时errno被设置 

(2)读写流:一旦读写成啦,光标会自动往后移动n个位置(n就是你读写成功的字节数)
	a:每次一个字符读写
		fgetc/getc/getchar
		fputc/putc/putchar
	b:每次一行读写
		fgets/gets 
		fputs/puts 
	c:直接读写,你想要读多少个对象都可以
		fread
		fwrite 
	
	(2.1)每次一个字符读写 
		fgetc/getc/getchar
		fputc/putc/putchar
		
		fgetc
		头文件 
			#include <stdio.h>
		函数功能
			用来从stream指定的文件流中,读取一个字符。
		函数原型
			int fgetc(FILE *stream);
		函数参数 
			FILE *stream	//指定要从哪个文件流中读取字符
		函数返回值 
			成功: 返回读到的哪个字符的ASCII码
			失败: 返回-1,同时errno被设置。
		


					
1.标准IO的函数接口 
(1)标准IO打开或关闭一个文件流 
	fopen 
	头文件
		#include <stdio.h>
	函数功能 
		用来打开一个普通文件(文本文件/二进制文件)
	函数原型
		FILE *fopen(const char *pathname, const char *mode);
	函数参数 
		const char *pathname		//要打开的那个文件的文件名
		const char *mode			//打开文件的方式,有如下几种: 
										"r":只读打开。文件不存在,则报错:
											打开后,光标在开头。
											
										"r+":读写打开,文件不存在,则报错; 
											打开后,光标在开头 
										"w":只写打开。文件不存在,则创建;
											打开后,文件内容截短(文件内容被清掉)
										"w+":读写打开,文件不存在,则创建; 
											打开后,文件内容截短(文件内容被清掉)
										"a":append 追加打开,文件不存在,则创建;
											打开后,光标在末尾。文件内容不会被截短
										"a+":读写打开。文件不存在,则创建。 
											原始读的位置在开头,原始写的位置在末尾
											
	函数返回值 
		成功:返回打开文件指针。FILE* 
				在标准IO中,FILE*代表一个打开的文件,后续
				标准IO库的函数都需要用到它
		失败:返回NULL,同时errno被设置。
		
	fclose:用来关闭stream指定的文件流	
	头文件
		#include <stdio.h>
	函数功能
		用来关闭stream指定的文件流 
	函数原型
		 int fclose(FILE *stream);
	函数参数
		FILE *stream		//要关闭的文件的FILE *
	函数返回值 
		 成功: 返回0 
		 失败: 返回-1,同时errno被设置 

(2)读写流:一旦读写成啦,光标会自动往后移动n个位置(n就是你读写成功的字节数)
	a:每次一个字符读写
		fgetc/getc/getchar
		fputc/putc/putchar
	b:每次一行读写
		fgets/gets 
		fputs/puts 
	c:直接读写,你想要读多少个对象都可以
		fread
		fwrite 
	
	(2.1)每次一个字符读写 
		fgetc/getc/getchar
		fputc/putc/putchar
		
		fgetc
		头文件 
			#include <stdio.h>
		函数功能
			用来从stream指定的文件流中,读取一个字符。
		函数原型
			int fgetc(FILE *stream);
		函数参数 
			FILE *stream	//指定要从哪个文件流中读取字符
		函数返回值 
			成功: 返回读到的哪个字符的ASCII码
			失败: 返回-1,同时errno被设置。								
			
		getc和fgetc一样,也是用来从stream指定的文件流中,
		读取下一个字符,getc和fgetc的区别在哪里? 
			fgetc函数;
			getc可能是用宏来实现的
			
			
		函数原型 
			 int getc(FILE *stream);

		getchar是用来从标准输入流stdin中获取下一个字符,
		并把读取到的字符的ASCII码返回
							
		函数原型 
			int getchar(void); 《==》 fgetc(stdin)
		--------------------------------------------------------
		头文件 
			#include <stdio.h>
		函数功能
			用来把c指定的字符,输出到stream指定的文件流中去
		函数原型
			int fputc(int c, FILE *stream);
		函数参数 
			int c			//要输入的字符的ASCII码
			FILE *stream	//文件流,表示输出到哪个文件中去
		函数返回值 
			成功:返回实际写入到文件流中的字符的ASCII码(c的ASCII码) 
			失败: 返回-1,同时errno被设置

		putc与fputc功能与返回值一样,只不过putc的实现
		可能是由宏来实现的。
		
		函数的原型 
			 int putc(int c, FILE *stream);

		putchar用来把c指定的字符,输出到“标准输出文件流中”
		函数原型 
			 int putchar(int c);  《==》fput(c,stdout)
		
	(2.2)每次一行读写 
		fgets/gets 
		fputs/puts
		
		头文件 
			#include <stdio.h>
		函数功能 
			用来从标准输入流(stdin)获取一行字符,存储到s指向的内存空间中去
		函数原型 
			char *gets(char *s);
		函数参数 
			char *s		//指向的空间,用来存储从输入缓冲区获取到的多个字符
		函数返回值 
			成功: 返回非NULL
			失败: 返回NULL,同时errno被设置
			
			注意: 
				gets有一个巨大的bug,你懂的!!!
				gets没有考虑到s指向的空间大小问题,存在越界的可能。
				所以从今天开始,gets你们就不要啦。
				
		fgets修正了gets的这个bug 
			
		函数功能 
			从stream所指向的文件中读取size个字节大小数据到s所指向的空间中
		函数原型
			char *fgets(char *s, int size, FILE *stream);
		函数参数 
			char *s				//指向的空间用来保存从文件流中读取到的数据
			int size			//表示你最多获取size个字节,size一般为s指向
									的空间的可用长度
									fgets输入结束有两种情况: 
										(1)遇到\n或文件结束 
										(2)已经读取到size-1个字符啦(后面留一个\0的位置)
			FILE *stream		//指针,表示从哪个文件流中读取数据				

		函数返回值 
			成功: 返回s的首地址
			失败: 返回NULL,同时errno被设置。
			
		-----------------------------------------------------------
		头文件
			#include <stdio.h>
		函数功能
			用来把s指向的字符串,输出到stream指定的文件流中去 
		函数原型
			 int fputs(const char *s, FILE *stream);
		函数参数
			const char *s		//指向要输出的字符串的首地址
			FILE *stream		//表示要输出到哪个文件流中去
		函数返回值 
			成功: 返回一个非负数
			失败: 返回-1,同时errno被设置
			
			puts用来把s指向的字符串,输出到
			标准输出流(stout)中去,会多输出一个\n
				
				
		函数原型 
			 int puts(const char *s);

	(2.3)直接读写 
		头文件 
		#include <stdio.h>
		函数功能
			从stream指向的文件流中,读取nmemb个对象
			而且每个对象size字节(nmemb),读到ptr指向的内存空间中去。
		函数原型
			 size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
		函数参数
			void *ptr			//指向的内存空间,用来保存从文件流读取的“数组”
			size_t size			//每个元素占的字节大小 
			size_t nmemb		//要读取nmemb个元素 
			FILE *stream		//表示要从哪个文件流中读取数据
		函数返回值 
			成功: 返回实际读取到的元素个!!! <= nmemb
			失败: 返回-1,同时errno被设置
		
		fwrite
		头文件 
			#include <stdio.h>
		函数功能
			用来把ptr指向的nmemb个元素(每个元素占size字节);写入
			stream指向的文件流中去
		函数原型
			size_t fwrite(const void *ptr, size_t size, size_t nmemb,
                 FILE *stream);
		函数参数 
			const void *ptr				//要写入的数组的首地址
			size_t size					//每个元素所占的大小
			size_t nmemb				//元素的个数 
			FILE *stream				//表示要写到哪个文件流中去
		函数返回值 
			成功: 返回实际写入到文件流中去的元素个数。 <= nmemb 
			失败: 返回-1,同时errno被设置

(3)冲洗流 fflush,	同步 
	头文件 
		#include <stdio.h>
	函数功能
		将缓冲区数据冲洗到对应的设备上或者丢弃(同步到硬件设备)
			对输出流,fflush把写缓冲区的内容写/更新到文件中去;
			对输入流,fflush把读缓冲区的内容直接discards(丢弃);
			stream为NULL,fflush把该进程所有打开的输出文件流同步。
			
	函数原型
		int fflush(FILE *stream);
	函数参数 
		FILE *stream			//要同步的文件流
	函数返回值 
		成功: 返回0 
		失败: 返回-1,同时errno被设置
		
		
	请分析如下程序的输出结果? 
		main()
		{
			printf("hello");	//把"hello"写 文件  stdout 的输出缓冲区
								中去了,但是并没有写入到外设文件中去。
								
			sleep(10);
			
			fflush(NULL);	//fflush(stdout);
							if stream 
								输出流	把输出缓冲区的内容写到外设文件中去
							if stream  输入流,把输入缓冲区的内容丢弃,这样的
								你下次读,就可用重新从外设文件中读啦

		}
	

(4)定位流 
	上面讲到的,fread/fwrite ......只是说要从哪个文件,读/写多少个字节的
	数据,并没有指定从文件流的哪个位置开始读写,标准IO库会为每个打开的
	文件流保存一个“文件偏移量”
	
	文件偏移量:offset  “光标”。直接确定下一个读/写的起始位置。
	一般来说,每次读或写之前,先要定位流。

	头文件
		#include <stdio.h>
	函数功能 
		定位一个文件的偏移量
	函数原型
		int fseek(FILE *stream, long offset, int whence);
	函数参数 
		FILE *stream			//文件流指针,表示你要定位哪个文件
		long offset				//偏移量,可正可负;要结合第三个参数 
		int whence				//从哪里开始定位 定位的方式, 有如下三种: 
									SEEK_SET:基于文件开头定位 
										新光标位置 = 文件开头 + offset(>= 0)
									SEEK_CUR:基于当前光标位置定位
										新光标位置 = 当前位置 + offset(可正可负)
									SEEK_END:基于文件的末尾定位
										新光标位置 = 文件末尾 + offset(可正可负)
	函数返回值 
		成功:返回0
		失败:返回-1,同时errno被设置

	ftell返回当前光标位置离文件开头有多少个字节
		函数原型 
			 long ftell(FILE *stream);

	rewind把文件光标,定位在文件开头

	函数原型 
		void rewind(FILE *stream);
			rewind(stream) <==> fseek(stream, 0, SEEK_SET);
(5)文件出错/结束标记
	头文件
		#include <stdio.h>
	函数功能 
		判断stream指定的文件流是否结束
	函数原型
		int feof(FILE *stream);
	函数参数 
		FILE *stream		//需要判断的文件指针
	函数返回值 
		如果返回真(非0) 文件到达末尾啦 
		如果返回假(0) 文件还没有到达末尾
	
		注意: 
			标准IO,在读到文件末尾时,会往缓冲区中填入一个EOF(二进制 11111111)
			
(6)格式化IO
	(6.1)格式化输入 
	scanf/sscanf/fscanf
	格式化输入 ? :按照我们指定的格式来输入。
	头文件 
		#include <stdio.h>
	函数功能
		按照我指定的格式来输入
	函数原型 								
		int scanf(const char *format, ...);  //scanf("%dawd%c%d%c",&a,&b,&c,&n); 
	函数参数
		const char *format, ...	
			scanf可以带很多个参数,scanf的参数可以分为两类: 
				第一个参数为第一类,格式化字符串,format string: 
				"格式化字符串"就是告诉用户怎么输入的,意思就是你
				必须按照它指定的格式输入。
				在格式化字符串中 有三类字符: 
					a.空白符(space tab)
						指示用户 你可以输入任意数量的空白符(包含0)
							scanf 把\n当作是输入结束
					b.非转义字符(普通字符,除空白符和%以外的字符)
						精准匹配,你的原样输入
					c.转义字符(以%开头的字符),有特殊含义	 
						%d -> [0-9]+
						%c -> 匹配一个字符(可以输入的字符)
						%f -> 浮点数 
						%s -> 字符串(不带空白符,scanf把空白符当作是一个分隔符)
						
				其它参数为第二类,地址列表: 
					格式化字符串中一个转义字符会定义一个地址,把一个转义字符的输入
					存储到指定的地址中去。
					如果转义字符的个数, 多余 地址个数 程序的行为将是
					undefined(未定义)	
	函数返回值 
		成功匹配的变量个数!!!
		
		
		scanf获取输入时,如何结束?scanf从stdin的读缓冲区中获取输入
					a.该输入的都输入完了
						scanf("abcd%d %cabcd",&a,&b);
							用户从终端输入: 
								abcd1234Aabcd =>该输入的都输入啦,scanf结束
					b.失败啦 
						scanf("abcd%d %cabcd",&a,&b)
							用户从终端输入: 
								ABCD -> scanf停止匹配啦
	
	例子: 
		int r;
		int a;
		char c;
		r = scanf("abcd%d %c",&a,&c);
			假设用户输入: 
				ABCD123 A 
				
		r = scanf("%d %c", &a,&c)
			假设用户输入: 
				123B
		
		sscanf它的功能与返回值,和scanf一样的,只不过, 
		sscanf的输入来源不是stdin,而是str指向的字符串
			sscanf的参数有三类: 
				1.str是输入来源字符串
				2.format是格式化字符串
				3.其它参数为地址列表
		
		
	函数原型 
		 int sscanf(const char *str, const char *format, ...);
	
	例子:  
		(1)const char *str = "1234BCDDDDD"
		int r; 
		int a;
		char c; 
		r = sscanf(str,"%d %c",&a,&c);
			
		fscanf它的功能和返回值,与scanf一样,只不过,fscanf它的输入
		来源不是stdin,而是stream指定的文件流,所以fscanf的参数,分为三类:
				1.第一个参数FILE *,指定输入来源
				2.第二个参数format,格式化字符串,与scanf是一样
				3.其它参数为 地址列表 
		
	函数原型 
		int fscanf(FILE *stream, const char *format, ...);
			scanf(format, ...); ==> fscanf(stdin,format, ...)
			
		例子: 找出文件中记录的分数最高的人的信息
			假设有文件stu.txt的内容如下  
				1	zhangsan	60
				2	lisi		90
				3	wangwu		80 
				...
				
			(1)
				struct student
				{
					int num;
					char name[32];
					int score;
				}
				
				struct student *p = malloc(sizeof(*p) * 100);
				FILE* fp = fopen("./stu.txt","r");
				
				int i = 0;
				int max_score = 0;
				int max_i = -1;
				while(!feof(fp))
				{
					fscanf(fp,"%d%s%d",&p[i].num,p[i].name, &p[i].score);
					if(p[i].score > max_score)
					{
						max_score = p[i].score;
						max_i = i;
					}
					i++;
				}
				
				fclose(fp);
				
				printf("%d %s %d\n",p[max_i].num,p[max_i].name,p[max_i].score);
				
				free(p);
				
	(6.2)格式化输出 
		prinft/sprintf/snprintf/fprintf
		
		"格式化输出"?:按照我指定的格式去输出。
		
		头文件
			#include <stdio.h>
		函数功能
			按照指定的格式去输出
		函数原型
			int printf(const char *format, ...);
		函数参数 
			const char *format, ...
			printf可以带多个参数,这么多参数,可以分为两类
			第一个参数为 第一类 格式化字符串:就是告诉你怎么输出的 
				格式化输出字符串有两类字符: 
					a.转义字符 :以%开头的字符 
						%d -> 按十进制有符号整数输出
						%u 
						%ld
						%lu
						%f 
						%c ->输出字符的形状 
						%s ->把后面的地址,字符串去输出,直到遇到\0
						....
					b.非转义字符 
						你得原样输出
				其它参数为第二类,要输出得变量或对象列表 
					要输出得变量或对象个数 应该与转义字符的个数一致	
		函数返回值
			实际打印的字符个数
				
		例子:  
			int a = 123;
			char c = 'A';
			int r; 
			r = printf("a = %d c = %c\n",a,c);
				
		fprinf它的功能和printf一样的,只不过 
		fprintf输出的不是输出到stdout,而是输出到stream指定的
		文件中。所以fprintf它的参数,可以分为三部分 
				第一个参数为FILE*,指定要输出到哪个文件中去 
				第二个参数 format,格式化字符串 
				其它参数,为要输出的变量或对象列表。
				
		返回值:为实际输出到文件中的字符个数		
		
		函数原型 
			 int fprintf(FILE *stream, const char *format, ...);

			printf(format, ...) => fprintf(stdout,format, ...);	
				
		sprintf它的功能和printf一样,只不过,sprintf 
		输出不是输出到stdout,而是输出到str指定的内存中去。
		所以sprintf参数,可以分为三部分: 
				第一个参数为char *,内存地址,指定要输出字符串的输出位置 
				第二个参数 format,格式化字符串 
				其它参数,为要输出的变量或对象列表。
			
			
					
		函数原型 
			 int sprintf(char *str, const char *format, ...);
		返回值: 
			为实际输出到内存中去的字符个数。
	
		
		注意: 
			sprintf有一个bug??
			str只是指定了一个内存起始地址,并没有限定它的内存范围。
			if输出字符串的长度>str 指向的内存的范围,越界
			=》内存的非法访问 
			
		so,为了解决sprintf的这个bug,才有了下面的snprintf
		
			sprintf中size指定str指向的那段内存空间的最大长度
			也就是说,格式化输出顶多输出size-1个字符到str
			指向的空间中去
		
	函数原型 
 int snprintf(char *str, size_t size, const char *format, ...);
				
   例子:   
char s[8] = {0};
		char *st = "1234567890";
int r;
r = snprintf(s, 7,"%s",st);
注意:snprintf它的返回值 是应该输出的字符串长度,而不是
实际输出到内存中的长度


2.作业: 
	利用标准IO实现copy两个普通文件




3.标准IO总结: 
	1.标准IO库是什么东西?为什么需要它?它的实现原理是什么?
	  标准IO与系统IO之间的关系?标准IO中用什么东西表示一个文件?
	  为什么把标准IO称之为流?为什么说标准IO的效率比系统IO高?

2.标准IO只能操作”普通文件“	!!!!
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值