我们已经有系统IO了,为什么还需要标准IO呢?
每个系统写出来都不一样,windows有windows的搞法
linux有linux的搞法.....
但是他们有一个共通点 --- 都是c写的
c就讲既然你们都是用我写的,那么我就得统一你们,这样我们的开发就变得简单很多
只要是支持c的系统都可以采用标准IO来处理文件
系统文件没有办法,只能按照系统io的搞法
普通文件的处理用标准io
规定 --- 以后的普通文件都采用标准io处理,系统级的文件采用系统io
标准io采用一个叫文件流的东西来表示这个打开的文件
typedef struct
{
char in[1024];//输入缓冲区
char out[1024];//输出缓冲区
......
}FILE;
输入缓冲区:系统io在硬盘里面读一个字节,那么就去硬盘读一个字节
FILE就不这么干,在硬盘上面读一个字节,标准IO会直接读一行数据(1024字节)
你现在不是需要一个字节吗,那么我就在缓冲区里面给你一个字节
对于用户来讲,它拿到的这个字节是在缓冲区里面拿的,因此对于用户来讲
去缓冲区里面拿取数据明显要快一些
输出缓冲区:FILE如果是写入,那么会首先写到这个缓冲区,
当缓冲区里面有大量的数据或者强制同步的时候才会往我们硬盘写入
缓冲区有几种
1 行缓冲
必须要往这个缓冲区里面写满一行才会同步到文件(fflush强制同步另算)
printf就是一个行缓冲
2 全缓冲
必须将缓冲区全部填满才会同步到文件
3 无缓冲
只要往缓冲区里面写入字节就会同步到文件
比较紧急的情况下面我们一般采用无缓冲
perror
在运行我们的程序的时候,系统会自动给我们打开三个文件
FILE * stdin;//标准输入
FILE * stdout;//标准输出
FILE * stderr;//标准出错
标准IO处理文件
打开文件
fopen
NAME
fopen, fdopen, freopen - stream open functions
SYNOPSIS
#include
FILE *fopen(const char *pathname, const char *mode);
FILE *fdopen(int fd, const char *mode);
pathname:打开的文件的路径名
fd:用文件描述符来表示你打开的这个文件
mode:以什么方式打开这个文件,本质是一个字符串
"r":只读打开,文件不存在则报错,打开后光标默认在开头
"r+":读写打开,文件不存在则报错,打开后光标默认在开头
"w":只写打开,文件不存在则创建,打开后文件的内容会被清空,光标在开头
"w+":读写打开,文件不存在则创建,打开后文件的内容会被清空,光标在开头
"a":追加打开,文件不存在则创建,打开后光标在末尾
"a+":读写打开,文件不存在则创建,原始读在开头,原始写在末尾
返回值:
成功返回FILE * 指针,后续对于这个文件的操作都是基于这个指针
失败返回NULL,同时errno被设置
NAME
fclose - close a stream
关闭一个文件流
SYNOPSIS
#include
int fclose(FILE *stream);
stream:你要关闭哪个流
读写文件
一次读写一个字节
NAME
fgetc, fgets, getc, getchar, ungetc - input of characters and strings
SYNOPSIS
#include
int fgetc(FILE *stream);//这个玩意儿是一个函数
int getc(FILE *stream);//它是一个宏
stream:你要在哪个文件流里面获取一个字节
获取到的这个字节都是通过返回值返回给你的
int getchar(void);//直接在stdin里面获取一个字符
getchar() fgetc(stdin)
NAME
fputc, putc, putchar - output of characters
往文件流里面写入一个字节
SYNOPSIS
#include
int fputc(int c, FILE *stream);//函数
int putc(int c, FILE *stream);//宏
int putchar(int c);//直接往stdout里面写入 putchar('A')fputc('A',stdout)
c:你要写入的这个字符
stream:你要往哪个文件流里面写入
返回值:
成功的时候c是什么返回的就是什么,
失败返回-1
文件读完,会自动往缓冲区里面填上一个字节 1111 1111 -> EOF
练习:
使用fopen的多种打开形式
r r+ w w+ a a+
往一个文件里面写入一个字节之后,弄一个while(1);看看这个字节有没有弄到文件里面去
用一次搬一个字节的方式,采用标准io拷贝一个文件
fp_from = fopen("1.txt","r");
fp_to = fopen("2.txt","w");
while(1)
{
char c = fgetc(fp_from);
if(c == EOF)
{
break;
}
fputc(c,fp_to);
}
读写一行
char *gets(char *s);//直接在stdout里面获取 这个函数有哦bug 可能会让s越界 不建议使用
char *fgets(char *s, int size, FILE *stream);//指定在哪个文件流
s:你获取到的数据放在哪个内存
size:你要获取多少个字节
stream:文件流
成功返回s,失败返回NULL,同时errno被设置
写一行数据到文件流
int fputs(const char *s, FILE *stream);
int puts(const char *s);//直接往stdout里面写入
puts("abcd") fputs("abcd",stdout)
s:写入的数据
stream:文件流
返回值:
puts() and fputs() return a nonnegative number on success, or EOF on
error.
指定字节去读,读二进制
NAME
fread, fwrite - binary stream input/output
二进制的读写
SYNOPSIS
#include
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
ptr:你要读到哪个内存
size: 你的一个元素有多少个字节
nmemb: 你有多少个元素
stream:文件流
size_t fwrite(const void *ptr, size_t size, size_t nmemb,
FILE *stream);
ptr:你要写入的内容
size: 你的一个元素有多少个字节
nmemb: 你有多少个元素
stream:文件流
成功返回实际读写的元素个数
int fseek(FILE *stream, long offset, int whence);//偏移
stream:文件流
offset:偏移量 跟lseek一样
whence:基准定位方式
SEEK_SET 开头
SEEK_CUR 当前位置
SEEK_END 末尾
成功返回0 失败返回-1,同时errno被设置(这里跟lseek不一样了)
long ftell(FILE *stream);//返回光标的位置到文件开头所有的字节数
fseek(fp,0x00,SEEK_END);
long filesize = ftell(fp);//求文件的大小
void rewind(FILE *stream);//直接将光标定位到开头
//刷新缓冲区
让流的缓冲区强制同步
对于输入流来说:fflush直接丢弃里面没有拿取完的数据(stdin比较特殊,fflush(stdin)可能会没有效果)
对于输出流来说:fflush会将没有同步到文件的数据强制同步到文件
程序如果正常结束,操作系统会自动给我们把所有的流都刷新
如果出现非正常死亡,如段错误,这个时候就不会刷新流了
NAME
fflush - flush a stream
刷新一个流
SYNOPSIS
#include
int fflush(FILE *stream);
stream:你要刷新哪个文件流
你可以填NULL,表示所有的流
练习:
用fread fwrite写一个copy文件,然后将我们的bmp文件处理改成fread
格式化的输入输出
给用于提供一个格式,用户通过这个格式来进行输入输出就很简单
NAME
scanf, fscanf, sscanf, vscanf, vsscanf, vfscanf - input format conver‐
sion
格式化输入
SYNOPSIS
#include
int scanf(const char *format, ...);//直接从stdin里面获取
int fscanf(FILE *stream, const char *format, ...);//从stream里面获取
int sscanf(const char *str, const char *format, ...);//从str这个字符串里面获取
stream:文件流
str:一个字符串 你再这里拿取有效的数据
format:格式化的字符串,里面的字符有三种
1 空白符
当成分隔符
你可以输入多个空白符,它只会当成一个分隔符
2 普通字符
用来做匹配,你需要原模原样的输入进来才能进行匹配
"abc%d" 你在输入的时候就必须先输入abc123 -> 这个时候%d才会匹配到123
3 转义字符 %
%d -> 整数
%c -> 字符
%f -> 浮点
......
%% -> %这个字符
每写一个%号,后面的...里面就多一个地址
scanf就是往这些地址里面写入数据的
... :地址,个数与%号一一对应,除非是%%
用,号隔开即可(参数)
返回值:
成功返回匹配到的个数,匹配是匹配转义字符,里面匹配几个就返回几个
scanf("%d%d%c"); -> 输入:123c123 -> 这个时候没有全部匹配 只匹配了一个 那么它就返回1
NAME
printf, fprintf, dprintf, sprintf, snprintf, vprintf, vfprintf, vdprintf, vsprintf, vsnprintf - formatted output conversion
格式化的输出
SYNOPSIS
#include
int printf(const char *format, ...);//固定往stdout里面输出
int fprintf(FILE *stream, const char *format, ...);
int dprintf(int fd, const char *format, ...);
int sprintf(char *str, const char *format, ...);//可能会造成str越界 因此建议使用下面的这个函数
int snprintf(char *str, size_t size, const char *format, ...);
stream:你要往哪一个文件里面输出 文件流
fd:你要往哪个文件描述符代表的那个文件里面输出
str:你要往哪个内存里面输出
size:你要输出多少个字节
format:格式化的字符串
1 空白符
2 普通字符
空白符 和 普通字符会原封不动的输出
3 转义字符 %
%d -> 整数 -> 整形值
%c -> 字符 -> 字符
%f -> 浮点 -> 小数
......
%% -> %这个字符
...:可变参数 与前面的%一一对应
这种格式化的输入输出我们用于带格式的文件里面
如一个文件里面保存了一些信息
前面是name 再是addr 接下来是age
penglei changsha 18
xiaoming zhongguo 45
......