标准IO:
在每个操作系统下,对于文件的处理方式、代码是不同的。
linux : open/read/
win : Winopen…
使用系统IO写代码,移植性很差。使用标准IO 能在不同的操作系统中实现需要的功能。
在标准IO库,用一个结构体(FILE)来描述或表示一个普通文件,然后再这个结构体中创建了两个缓冲区(两端内存),一个读缓冲区,一个写缓冲区。
FILE
{
char * in; //指向读的缓冲区的
char * out;//指向写的缓冲区的
...
...
}
对“文件”操作的接口函数:
fopen/fclose/fread/fwrite/fseek...
puts/gets/fputs/fgets/printf/scanf....
APP通过标准IO操作文件的流程:
APP -》 标准IO库 -> 对应平台的系统IO -> OS(内核) ->Hardware
FILE 有两个缓冲区(标准IO库开辟的两段内存)
*in -----> 读的缓冲区
*out ----> 写的缓冲区
缓冲区:
标准IO带缓冲IO, IO流,它的速率要比系统IO要高 。
系统IO:
read 1B 从硬盘读一个字节
标准IO:
fread 1B ,它会从硬盘读一块(512B)出来,放到
标准IO的读缓冲区
标准IO缓冲区有三种类型:
行缓冲:
数据达到一行啦,同步到外设上面去。
假设您设置一行最多80字节。
遇到了\n(换行符,一行结束的标志)
当你缓冲区里面有了80个字节的数据啦,这个时候也会同步到外设上面去。
printf
全缓冲:
缓冲区中的数据要填满整个缓冲区,才同步到外设上去。
无缓冲:
缓冲区有一个字节,就会同步到外设。
perror -> 无缓冲
对于缓冲区也是可以自己去设置(setbuf)
系统IO:操作系统会自动的为每一个进程打开三个文件:
标准输入文件 文件描述符 STDIN_FILENO (0)
标准输出文件 文件描述符 STDOUT_FILENO (1)
标准出错文件 文件描述符 STDERR_FILENO (2)
标准IO库,会自动为每一个进程,打开三个标准io流(文件):
标准输入流: FILE * stdin //scanf() <------ stdin
stdin是定义在<stdio.h>中的一个全局变量,它指向
标准输入设备(一般为终端或键盘)
标准输出流: FILE * stdout //printf() <------ stdout
stdout是定义在<stdio.h>中的一个全局变量,它指向
标准输出设备(一般为控制台,终端或屏幕)
标准出错: FILE * stderr //perror() <---- stderr
stderr是定义在<stdio.h>中的一个全局变量,它指向
标准出错设备(一般为终端)
2.标准IO的函数接口
1.打开关闭一个IO流 用来打开一个普通文件(文本文件/二进制文件)
函数原型:
FILE *fopen(const char *pathname, const char *mode);
函数参数:
pathname:要打开的那个文件的文件名(带路径)
mode: 打开文件的方式,有如下几种方式:
"r" : 只读打开。 文件不存在,则报错;打开后,光标在文件开头。
"r+" : 读写打开。 文件不存在,则报错;打开后,光标在文件开头。
"w" : 只写打开。文件不存在,则创建,打开后,文件内容截短(文件内容被清空)
"w+" : 读写打开。文件不存在,则创建,打开后,文件内容截短(文件内容被清空)。
"a" : append 追加打开(只写打开)。 文件不存在,则创建 打开后,光标在文件末尾。文件内容不会被截短。
"a+" : 读写打开。文件不存在,则创建 ;
原始读的位置在文件开头,原始写的位置在文件末尾
(只有一个光标,打开后如果先读,光标在文件开头;如果先写,光标在文件的末尾)
返回值:
成功返回打开文件指针。 FILE *
在标准IO中,FILE*代表一个已经打开的文件,后续的
标准io库的函数都需要用到它。
失败返回NULL,同时errno被设置。
2.关闭IO流
函数原型 :
int fclose(FILE * stream);
stream : 你想要关闭的那一个文件流
返回值:成功返回 0
失败返回-1,同时error 被设置
3.读写一个文件
一旦读写成功,光标就会挪动到读写的位置。
a.每次读写一个字节
fgetc / getc / getchar
fputc / putc / putchar
>>>>>>>>>>> 读 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
函数原型:
int fgetc(FILE *stream);
函数参数:
stream: 指定您想要到那个文件里面去读
返回值: 、
成功返回读到的那个字符的ASCII
失败返回-1,同时errno被设置。
------------------------------------------------------------------------------------
getc和fgetc一样,也是用来从stream指定的文件流中
读取一个字符,并将读到的那个字符的ASCII返回。
getc和fgetc那么它们的区别?
fgetc是一个函数,
getc可能是用宏来实现的
函数原型:
int getc(FILE *stream);
函数参数:
stream: 指定您想要到那个文件里面去读
返回值:
成功返回读到的那个字符的ASCII
失败返回-1,同时errno被设置。
------------------------------------------------------------------------
getchar是用来从标准输入流(stdin)中获取下一个字符,
并将读到的那个字符的ASCII返回。
int getchar(void);
getchar() <=> fgetc(stdin)
<<<<<<<< 写 <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
fputc用来把c指定的字符,输出到stream指定的文件流。
函数原型:
int fputc(int c, FILE *stream);
函数参数:
c: 要输入到文件流中去的那个字符的ASCII
stream: 指定您想要输入到哪个文件流中去
返回值:
成功返回实际写入到文件流中的字符的ASCII
失败返回-1,同时errno被设置。
----------------------------------------------------------------------------
putc和fputc是一样的,功能一样,返回值,参数全都一样
但是putc可能是用宏来实现的。
int putc(int c, FILE *stream);
-----------------------------------------------------------------------------
putchar用来把c指定的字符,输出到标准输出文件流中
putchar(c) <=> fputc(c, stdout)
int putchar(int c);
b.每次读写一行
fgets / gets
fputs / puts
>>>>> 读 >>>>>>>>>>>>>>>>>>>>>>>>
gets:
函数原型:
char *gets(char *s); //此函数未考虑到内存的大小,存在越界的风险
函数参数:
s: 指向一个可写空间,用来保存从输入缓冲区读到的多个字符
返回值:
成功返回s的首地址。
失败返回NULL,同时errno被设置。
-----------------------------------------------------------------------------
fgets :
函数原型:
char *fgets(char *s, int size, FILE *stream);
函数参数:
s:指向的空间用来保存从文件流中去读的数据
size: 表示您最多获取size个字节,size 一般是s指向的空间的可用长度。
fgets输入结束有两种情况:
(1) 遇到\n或文件结束
(2) 已经读取到了size-1个字节(后面留一个位置给\0)
stream: 表示您从哪个文件流中去读
返回值:
成功返回s的首地址
失败返回NULL,同时errno被设置。
>>>>> 写 >>>>>>>>>>>>>>>>>>>>>>>>
fputs/puts
fputs把s指向的字符串,输出到stream指定的文件流。
函数原型:
int fputs(const char *s, FILE *stream);
函数参数:
s: 指向您要输出到文件流中去的字符串的首地址
stream: 表示要输出到哪个文件流
返回值:
成功返回非负数
失败返回-1,同时errno被设置。
-------------------------
puts : 用来把s指向的字符串,输出到标准输出流(stdout)中去,会多输出一个\n
函数原型:
int puts(const char *s);
函数参数:
s: 指向您要输出到标准输出流中去的字符串的首地址
返回值:
成功返回非负数
失败返回-1,同时errno被设置。
puts(s) => fputs(s, stdout) + putc('\n')
c.直接任意读写,二进制读写
fread / fwrite
函数原型:
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
函数参数:
ptr: 指向的内存空间,用来保存从文件流读取到的数据"数组"
size: 每一个元素占字节大小
nmemb:要读取n个元素
stream:表示您要到哪个文件流中去读
返回值:
成功返回实际上读到的元素的个数!!! <= nmemb
失败返回-1,同时errno被设置。
----------------------------------------------------
fwrite用来把ptr指向的n个元素(每一个元素占size字节)写到stream指向的文件流中去。
函数原型:
size_t fwrite(const void *ptr, size_t size, size_t nmemb,
FILE *stream);
函数参数:
ptr: 指向的空间,用来保存您将要写入的内容
size: 每一个元素的大小
nmemb : 要写入多少个元素
stream: 您要写入到哪个文件流中去
返回值:
成功返回实际写入到文件流中的元素个数 。 <= nmemb
失败返回-1,同时errno被设置。
4.冲洗一个文件流
把缓冲区的内容做一次与硬件的同步
函数原型:
int fflush(FILE * stream);
stream:要同步的那一个流。
返回值:
成功返回0
失败返回-1,同时error被设置
注意:
对于输出流,fflush把写缓冲区的内容写/更新到文件中去
对于输入流,fflush把读缓冲区的内容直接丢弃。
so下一次读就会重新文件中去读取内容。
stream为NULL,fflush把该进程所有的打开的输出文件流同步。
5.定位文件流
文件偏移量”: offset 光标。 文件读取到的位置。
fseek用来定位一个文件流的光标位置的
函数原型:
int fseek(FILE *stream, long offset, int whence);
函数参数:
stream: 文件流指针,您要定位的是那一个文件。
offset:偏移量,具体含义与第三个参数相关:
whence: 光标的定位方式:
SEEK_SET : 基于文件开头定位
新光标的位置 = 文件的开头 + offset
SEEK_CUR : 基于光标的当前位置定位
新光标的位置 = 当前位置 + offset(可正可负)
SEEK_END : 基于文件末尾定位
新光标的位置 = 文件的末尾 + offset(可正可负)
返回值:
成功返回0
失败返回-1,同时errno被设置。
函数:
ftell返回当前位置离文件开头有多少个字节。
long ftell(FILE *stream);
rewind把文件光标,定位在文件开头。
void rewind(FILE *stream);
6.文件出错/文件结束标志
EOF: End Of File 文件结束标志 “宏”
feof:判断stream指向的文件流是否结束
函数原型:
int feof(FILE *stream);
函数参数:
stream
返回值:
返回真(非0) 如果文件达到末尾啦
返回假(0) 如果文件还没有达到末尾啦
NOTE:
标准IO库,在读到文件末尾时,会往缓冲区填入EOF
EOF 二进制 11111111
7. 格式化输出/输入
7.1) 格式化输入
scanf/sscanf/fscanf
"格式化输入" : 按照我指定的格式来输入
int scanf(const char *format, ...);
scanf可以带很多参数,scanf参数一般分为两类:
第一个参数为第一类参数:格式化字符串
“格式化字符串”就是告诉用户该怎么输入参数,你得
按照它所指定的格式去进行输入。
在“格式化字符串中有三类字符”:
a、空白符(空格 tab。。)
指示用户 您可以输入任意多个空白符(包含0)scanf是\n当作输入结束.
b、非转义字符
(普通字符,除了空白符和%意外的字符)
精准匹配,您得原样输入
c、转义字符(以%开头的)
%d -> [0-9]+
%c -> 匹配一个字符(可以输入的字符)
%f -> 浮点数
%s -> 字符串(不带空白符,scanf会把空白符当作是分隔符)
...
其它参数为第二类参数,地址列表:
格式化字符串中一个转转义字符会对应一个地址,把一个
转义字符的输入存储到指定的地址中去。
如果转义字符的个数 多于 地址个数,程序行为是Undefined
scanf获取输入时,从stdin的缓冲区中获取输入,输入何时结束?
a、该用户的都输入完啦
scanf("abcd%d %c1234", &a, &b);
用户输入:
abcd1234A1234 -> 该用户的都输入了 , scanf结束
b、 匹配失败
scanf("abcd%d %c123", &a, &b);
用户输入:
ABCD -> scanf停止匹配
返回成功匹配到的变量的个数。
r = scanf("%d%c",&a, &c);
123B456
a = ?123
c = ? B
r = ? 2
------------------------------------ ------------------------------------
int sscanf(const char *str, const char *format, ...);
sscanf它的功能以及函数的返回值都是和是一样的,只不过sscanf的输入来源不是stdin,而是来自str指向的哪个字符串
sscanf三类参数:
str: 输入字符串的来源
format:是格式化字符串
.... : 地址列表
const char * str = "1234BCDDDD"
int r,a;
char c;
r = sscanf(str, "%d %c", &a, &c);
a = 1234
c = B
r = 2
------------------------------------------------------------------------------------
int fscanf(FILE *stream, const char *format, ...);
fscanf它的功能还有返回值都是scanf一样,只不过fscanf它的输入从stream指向的那一个文件流中去获取。
fscanf参数三类:
stream: 指向的哪个文件流
format:是格式化字符串
.... : 地址列表
fscanf(stdin, fromat, ...) <=> scanf(format, .....)
7.2 )格式化输出
printf/sprintf/fprintf/snprintf
"格式化输出" : 按照您自己指定的格式输出
int printf(const char *format, ...);
printf可以带两类参数:
(1) 格式化字符串:就是告诉你按照什么样的格式输出
a、转义字符:以%开头的字符 \n
%d %u %ld %lu %f %c %s
.....
b、非转义字符
原模原样的输出
(2) 要输出的对象的地址列表
要输出的变量或对象的个数 应该与 转义字符(%)的个数一致
返回值:
实际打印的字符的个数
------------------------------------------------------------------------------------
int fprintf(FILE *stream, const char *format, ...);
fprintf它的功能和返回值,和printf一样,只不过fprintf输出不是输出到stdout,而是输出到stream指向那个文件流中。
fprintf有三类参数:
stream: 指向输出的文件流
format: 格式化字符串
.....: 要输出的对象的地址列表
返回值:
实际输出到文件流中去的字符的个数
------------------------------------------------------------------------------------
int sprintf(char *str, const char *format, ...);
sprintf它的功能和返回值,和printf一样,只不过sprintf输出不是输出到stdout,而是输出到str指向那个内存中去。
sprintf有三类参数:
str: 指向您要输出的地址
format: 格式化字符串
.....: 要输出的对象的地址列表
返回值:
实际输出到内存中去的字符的个数
sprintf存在越界的风险!
int snprintf(char *str, size_t size, const char *format, ...);
snprintf它的功能,和sprintf一样,只不过snprintf限制了大小。
sprintf有四类参数:
str: 指向您要输出的地址s
size: szie是str指向的那段内存的最大长度,顶多输出size-1个字符到str中
format: 格式化字符串
.....: 要输出的对象的地址列表
返回值:
返回应该输出的字符串的长度,而不是实际
上输出字符串的长度