1、标准IO编程——缓冲IO
1.1、概念
流:所有的数据的输入或者输出仅是简单的从程序移进或者移出,这种字节流,就称为流。
流分类:文本流、二进制流
文件指针(FILE指针、即FILE结构体指针)
每个被使用的文件都在内存中开辟一个结构体空间,用来存放文件的有关信息,该结构体类型是由系统定义的,取名为FILE。
标准I/O库的所有操作都是围绕流(stream)来进行的,在标准I/O中,流用FILE *来描述。
标准I/O库是由Dennis Ritchie在1975年左右编写的。
//当执行一个程序时,会默认打开三个文件,分别是:标准输入,标准输出,标准错误输出 ,这三个文件会对应三个文件指针
文件指针 文件描述符
标准输入 stdin 0 --- STDIN_FILENO
标准输出 stdout 1 --- STDOUT_FILENO
标准错误输出 stderr 2 --- STDERR_FILENO
1.2、标准IO操作文件步骤
第一步:打开文件 ----- 获取文件指针
第二步:对文件进行读写操作
第三步:关闭文件
1.3、标准I/O - 带缓冲I/O
(带缓冲的目的:尽量减少使用read/write的调用)
在进行标准IO时,系统自动在内存中为每一个正在使用的文件开辟一个缓冲区,从内存向磁盘输出数据必须先送到内存缓冲区。
缓冲分三种情况:!!!
1.3.1、全缓存 -----常规文件
向文件中写数据或者从文件中读数据时,数据先保存到缓冲区中。
只有当缓冲区满或使用fflush()刷新或关闭文件时,才会将缓冲中的数据刷新到文件或程序中。
1.3.2、行缓存 -----标准输入(键盘) 标准输出(屏幕)
向标准输出中写数据或者从标准输入中读数据时,数据先保存到缓冲区中。
只有当缓冲区中出现换行符’\n’ 或 使用fflush()刷新 或 关闭文件时时, 才会将缓冲中的数据刷新到标准输出或程序中。
1.3.3、不缓存 -----标准错误输出(屏幕)
向标准错误输出写数据时,数据只要写到缓冲区中,就会立即被刷新。
int main(void)
{
//fprintf(stdout,"hello world"); //通过标准输出打印数据:行缓冲----屏幕上看不到数据
fprintf(stderr,"hello world"); //通过标准输出打印数据:无缓冲--屏幕上可以看到数据
while(1);
return 0;
}
1.4、标准IO打开文件的方法
FILE *fopen(const char *path, const char *mode);
//参数1:path ------要打开的文件
//参数2:mode ------打开的方式(操作权限)
"r" 只读 文件必须存在
"r+" 可读可写 文件必须存在,从文件开头开始覆盖文件原先的内容。
"w" 只写 文件可以不存在,如果不存在则创建,如果文件存在,则将文件清空
"w+" 可读可写 文件可以不存在,如果不存在则创建,如果文件存在,则将文件清空
"a" 只写 文件可以不存在,如果不存在则创建,如果文件存在,在文件末尾追加
"a+" 可读可写 文件可以不存在,如果不存在则创建,如果文件存在,在文件末尾追加
//返回值:成功返回FILE*,失败:NULL
例如:
int main(void)
{
FILE * fp;
fp = fopen("1.txt","a"); //大家可以尝试使用不同的权限测试: r r+ w w+ a a+
if(NULL==fp){
perror("fopen");
exit(1);
}
return 0;
}
1.5、标准IO关闭文件的方法
int fclose(FILE *fp);
//参数:fp -----FILE*类型的文件指针
例如:
int main(void)
{
FILE * fp;
//打开文件
fp = fopen("1.txt","a");
if(NULL==fp){
perror("fopen");
exit(1);
}
//关闭文件
fclose(fd);
return 0;
}
1.6、格式化标准IO
数据在以字节流的形式传递过程中需要按照一定的格式传递。
1.6.1、格式化输入
int scanf(const char *format, ...); //从键盘输入数据给程序
int fscanf(FILE *stream, const char *format, ...); //从stream指向的文件中获取数据给程序
//例如:
struct student{
int sno;
char name[20];
float score;
};
int main(void)
{
FILE * fp;
int n,i;
printf("请输入学生的人数:");
scanf("%d",&n);
struct student st[n];
//打开文件
fp = fopen("1.txt","r+");
if(NULL==fp){
perror("fopen");
exit(1);
}
for(i = 0; i < n; i++)
//fscanf(fp,"sno=%d,name=%s score=%f\n",&st[i].sno,st[i].name,&st[i].score);
fscanf(fp,"%d%s%f\n",&st[i].sno,st[i].name,&st[i].score);
for(i = 0; i < n; i++)
printf("sno=%d name=%s score= %.2f\n",st[i].sno,st[i].name,st[i].score);
return 0;
}
int sscanf(const char *str, const char *format, ...); //从str指向的内存中获取数据给程序
//例如:
struct student{
int sno;
char name[20];
float score;
};
int main(void)
{
FILE * fp;
struct student st;
char buf[] = "1001 张三 98.56";
sscanf(buf,"%d%s%f",&st.sno,st.name,&st.score); //从buf中读取不同类型的数据
printf("sno=%d,name=%s,score=%.2f\n",st.sno,st.name,st.score);
return 0;
}
1.6.2、格式化输出
int printf(const char *format, ...); //向标准输出(屏幕)打印数据
int fprintf(FILE *stream, const char *format, ...); //向stream指向的文件中输出数据
//例如:
struct student{
int sno;
char name[20];
float score;
};
int main(void)
{
FILE * fp;
int n,i;
printf("请输入学生的人数:");
scanf("%d",&n);
struct student st[n];
//打开文件
fp = fopen("1.txt","a+"); //以追加方式打开文件
if(NULL==fp){
perror("fopen");
exit(1);
}
for(i = 0; i < n; i++){
scanf("%d%s%f",&st[i].sno,st[i].name,&st[i].score); //从键盘获取一条信息
fprintf(fp,"%d\t%s\t%.2f\n",st[i].sno,st[i].name,st[i].score); //向文件中写一条信息
}
return 0;
}
int sprintf(char *str, const char *format, ...); //向str指向的内存中写数据,有可能导致溢出
int snprintf(char *str, size_t size, const char *format, ...); //向str指向的内存中写size个字节数据,size表示内存空间的大小
//例如:
struct student{
int sno;
char name[20];
float score;
};
int main(void)
{
FILE * fp;
struct student st = {1001,"张三",98.56};
char buf[100];
sprintf(buf,"%d,%s,%.2f",st.sno,st.name,st.score); //可以将不同类型的数据转换成一个字符串
printf("%s\n",buf);
return 0;
}
1.7、非格式化标准IO
数据以字节流的形式在程序和文件之间传递。
1.7.1、一次读写一个字符的IO方法
一次读一个字符
int fgetc(FILE *stream); //函数:从stream指向的文件中读一个字符
int getc(FILE *stream); //宏:从stream指向的文件中读一个字符
int getchar(void); //函数:从标准输入(键盘)读取一个字符
//函数的返回值:读到的字符的编码值
//清空输入缓冲区!!!
while(getchar() != '\n');
//例如:
int main(int argc,char **argv)
{
FILE * fp;
char ch;
//1,打开文件
fp = fopen(argv[1],"r"); //以追加方式打开文件
if(NULL==fp){
perror("fopen");
exit(1);
}
//2,读取文件中的内容
while((ch = fgetc(fp)) != EOF){
printf("%c",ch);
}
//关闭文件
fclose(fp);
return 0;
}
一次写一个字符
int fputc(int c, FILE *stream); //函数:将字符c写入到stream指向的文件中
int putc(int c, FILE *stream); //宏:将字符c写入到stream指向的文件中
int putchar(int c); //函数:将字符c写到标准输出(屏幕)
//向屏幕输出一个字符
putchar('A');
//例如:
int main(int argc,char **argv)
{
FILE * fp;
char str[20];
int i;
//1,打开文件
fp = fopen(argv[1],"w"); //以追加方式打开文件
if(NULL==fp){
perror("fopen");
exit(1);
}
//2,向文件中的写一个字符串
printf("请输入一个字符串:");
scanf("%s",str);
for(i = 0; str[i] != '\0';i++)
fputc(str[i],fp);
//关闭文件
fclose(fp);
return 0;
}
1.7.2、一次读写一行的IO方法
一次读一行
char *fgets(char *s, int size, FILE *stream); //从stream指向的文件中读一行数据保存到s指向的空间中
//参数1:s ----- 内存的地址
//参数2:size -- 内存空间大小
//参数3:stream--- 文件指针
//返回值:成功返回s,失败或者到文件末尾:NULL
//特别注意:
//fgets()函数在读取数据时,如果一行数据的长度大于size,则只接收size-1个字符,剩余的字符,会在下一次调用时读取,直到换行符'\n';如果一行数据的长度小于size,则读到换行符停止读取。
//当fgets()读取一个完整的行时,连同换行符一起读到s中。
char *gets(char *s); //从标准输入获取一行数据保存到s指向的空间中,不会读取后面的换行符
//例如
int main(void)
{
char buf[20];
//fgets(buf,sizeof(buf),stdin); //会接收换行符
gets(buf); //不会接收换行符
printf("%s",buf);
return 0;
}
int main(int argc,char **argv)
{
FILE * fp;
char buf[100];
//1,打开文件
fp = fopen(argv[1],"r"); //以追加方式打开文件
if(NULL==fp){
perror("fopen");
exit(1);
}
//2,一次读一行
while(fgets(buf,sizeof(buf),fp) != NULL){
printf("%s",buf);
}
//关闭文件
fclose(fp);
return 0;
}
一次写一行
int fputs(const char *s, FILE *stream); //将s指向的字符串写入stream指向的文件中
//参数1:s ----- 内存的地址
//参数2:stream--- 文件指针
int puts(const char *s); //将s指向的字符串打印到标准输出(屏幕)
//例如:
int main(int argc,char **argv)
{
FILE * fp;
char str[20];
int i;
//1,打开文件
fp = fopen(argv[1],"w"); //以追加方式打开文件
if(NULL==fp){
perror("fopen");
exit(1);
}
//2,向文件中的写一个字符串
printf("请输入一个字符串:");
scanf("%s",str);
fputs(str,fp); //向文件中写一行数据
//关闭文件
fclose(fp);
return 0;
}
1.7.3、一次读写一块的IO方法
一次读一块
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream); //从stream指向的文件中获取数据
//参数1:ptr ------存放数据的内存空间地址
//参数2:size -----对象的大小
//参数3:nmemb ----对象的个数
//参数4:stream ----文件指针
//返回值:成功--读的对象的个数,失败:0
//例如:
struct student{
int sno;
char name[20];
float score;
};
int main(int argc,char **argv)
{
FILE * fp;
int a[5],i;
struct student st[3];
//1,打开文件
fp = fopen(argv[1],"r"); //以追加方式打开文件
if(NULL==fp){
perror("fopen");
exit(1);
}
//2,读取文件中的内容
#if 0
fread(a,sizeof(int),5,fp);
for(i = 0; i < 5; i++)
printf("%d\t",a[i]);
printf("\n");
#else
fread(st,sizeof(struct student),2,fp);
for(i = 0; i < 2; i++)
printf("%d %s %.2f\n",st[i].sno,st[i].name,st[i].score);
#endif
//关闭文件
fclose(fp);
return 0;
}
一次写一块
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream); //向stream指向的文件中写数据
//参数1:ptr ------存放数据的内存空间地址
//参数2:size -----对象的大小
//参数3:nmemb ----对象的个数
//参数4:stream ----文件指针
//返回值:成功--读的对象的个数,失败:0
例如:
struct student{
int sno;
char name[20];
float score;
};
int main(int argc,char **argv)
{
FILE * fp;
//int a[5] = {10,20,30,40,50};
struct student st[] = {{1001,"Jack",98.45},{1002,"Rose",97.89}};
int i;
//1,打开文件
fp = fopen(argv[1],"w"); //以追加方式打开文件
if(NULL==fp){
perror("fopen");
exit(1);
}
//2,向文件中的写对象
#if 0
fwrite(a,sizeof(int),5,fp); //写5个int型对象到文件中
#else
fwrite(st,sizeof(struct student),2,fp); //写5个int型对象到文件中
#endif
//关闭文件
fclose(fp);
return 0;
}
1.8、总结
//标准IO中常用的函数
fopen() fclose()
getchar() fgets() puts()
fread() fwrite()
对象 | 对象 | \n | ||
---|---|---|---|---|
文件打开和关闭 | ||||
fopen | fclose | |||
格式化 | ||||
scanf | 从标准输入输入数据 | printf | 标准输出 | 不读取,不补充 |
fscanf | 从文件获取数据 | fprintf | 文件 | 不读取,不补充 |
sscanf | 从内存(读取不同类型的数据) | sprintf | 内存 | |
snprintf | 内存 | |||
非格式化 | ||||
单个字符 | ||||
fgetc | 文件 | fputc | 文件 | |
getc | 文件 | putc | 文件 | 读取,不补充 |
getchar(不是函数,而是宏) | 标准输入 | putchar(不是函数,而是宏) | 标准输出 | 读取,不补充 |
行 | ||||
fgets | 文件 | fputs | 文件 | 读取,不补充 |
gets | 标准输入 | puts | 标准输出 | 不读取,补充 |
块 | ||||
fread | fwrite |