第一天
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只能操作”普通文件“ !!!!
01-12
595
![](https://csdnimg.cn/release/blogv2/dist/pc/img/readCountWhite.png)
07-21
07-21
07-10
“相关推荐”对你有帮助么?
-
非常没帮助
-
没帮助
-
一般
-
有帮助
-
非常有帮助
提交