Linux应用(2)——标准IO
一、简介
第一类:C语言阶段操作过的--只能对特殊文件操作
scanf printf getchar putchar gets puts
第二类:新增的--可以对特殊文件操作,也可以是对普通文件操作
fopen fclose fread fwrite fseek
fscanf fprintf fgetc fputc fgets fputs
feof fflush
1.1 文件IO特点
- 文件IO可以对普通文件操作,也可以对特殊文件操作
- 文件会存在文件指针,当open打开文件时,文件指针默认在文件开始处,对文件的读写,都会造成文件指针的偏移(以字节为单位),当close关闭后,文件指针默认在文件末尾
- 文件IO的读写函数没有缓冲区,读写是马上生效的,是一种低速IO
- read 输入--> 文件数据的读取 write 输出--->数据存放到文件
1.2 标准IO特点
- 标准IO可以对普通文件操作,也可以对特殊文件操作
- 文件会存在文件指针,当fopen打开文件时,文件指针默认在文件开始处,对文件的读写,都会造成文件指针的偏移(以字节为单位),当fclose关闭后,文件指针默认在文件末尾
- 标准IO的读写函数存在缓冲区,读写不会马上生效,当满足条件才生效,是一种高速IO
- 标准IO存在多种读写方式,读函数---输入 写函数---输出
标准IO 的读写通常配套使用
第一对读写:fscanf fprintf 格式化读写
第二对读写:fgetc fputc 单字符读写
第三对读写:fgets fputs 字符串读写
第四对读写:fread fwrite 全缓存读写
1.3 缓存特点
分类:
---针对的读写函数
无缓存: 文件IO 中的 read write
行缓存: 标准IO 剩余的都是行缓存
scanf printf getchar putchar gets puts
fscanf fprintf fgetc fputc fgets fputs
全缓存: 标准IO fread fwrite
行缓存满足条件
在常规开发过程中,用户无需记忆缓存的满足条件,因为默认常规下都是满足的
------满足以下任意一个即可
输入输出存在换行符输入: ---回车结束;
输出: ---输出 ‘\n’
- 程序自然结束,没有死循环
- 程序中采用 fclose关闭文件
- 程序中采用fflush 刷新缓冲区
- 写满该行 大致 1024字节
全行缓存满足条件
------满足以下任意一个即可
- 程序自然结束,没有死循环
- 程序中采用 fclose关闭文件
- 程序中采用fflush 刷新缓冲区
- 写满全缓存 大致 4K 字节
1.4 文件IO与标准IO对比
对比 | 文件IO | 标准IO |
用途上 | 用途1:实现应用层数据的存取,不丢失 用途2:实现驱动和应用的数据交流 | 用途1:实现应用层数据的存取,不丢失 用途2: 实现驱动和应用的数据交流 |
对文件操作上 | 可以对普通文件操作,也可以对特殊文件操作;普通文件需要先打开在读写;特殊文件直接读写 | 可以对普通文件操作,也可以对特殊文件操作;普通文件需要先打开在读写;特殊文件直接读写 |
文件指针 | 文件会存在文件指针,当open打开文件时,文件指针默认在文件开始处,对文件的读写,都会造成文件指针的偏移(以字节为单位),当close关闭后,文件指针默认在文件末尾 | 文件会存在文件指针,当fopen打开文件时,文件指针默认在文件开始处,对文件的读写,都会造成文件指针的偏移(以字节为单位),当fclose关闭后,文件指针默认在文件末尾 |
缓冲区 | 无缓存,读写马上生效,低速IO | 有缓存,读写满足条件才生效,高速IO |
文件ID | int fd 文件描述符 | FILE *fp 文件流指针 |
应用平台 | #include <unistd.h> 只能在linux环境下运行 | #include <stdio.h> 可以在linux环境下运行,也可以在win下运行 |
特殊文件 | 文件IO--文件描述符/fd | 标准IO--文件流指针/fp |
标准输入 | 0 | stdin |
标准输出 | 1 | stdout |
标准错误 | 2 | stderr |
普通文件 | 默认从3开始分配 |
二、函数
2.1 fopen,fclose
打开/关闭函数应用
头文件 :#include<stdio.h>
函数原型:FILE * fopen(const char * path,const char * mode)
函数参数:
@param1
const char * path : 带路径的文件名
@param2
const char * mode : 打开权限
r : 只读方式,打开已存在的文件
w : 覆盖方式打开只写文件,如果文件存在就覆盖在打开,如果文件不存在就创建
a : 追加方式打开只写文件,如果文件存在就追加在打开,如果文件不存在就创建
+: 可读可写
b : 以二进制方式
r+ 可读可写方式,打开已存在的文件
w+ 覆盖方式打开 可读可写文件,如果文件存在就覆盖在打开,如果文件不存在就创建
a+ 追加方式打开可读可写文件,如果文件存在就追加在打开,如果文件不存在就创建
注意事项: 'r' 错误的 ; "r" 正确的
函数返回值:
成功: 返回文件的ID--文件流指针
失败: NULL
函数功能:以指定方式(是否存在、文件权限) 打开文件
函数特性:
fopen成功后,文件指针默认在文件开始处
可以利用对同个文件的多次打开,实现把文件指针偏移到文件开始处;同样也可以利用fseek函数实现
头文件:#include<stdio.h>
函数原型:int fclose(FILE * stream);
函数参数:FILE * stream 文件流指针
函数返回值: 应用不多
若关文件动作成功则返回 0,有错误发生时则返回 EOFstruct _IO_FILE {
函数功能:关闭文件
2.2 fgetc,fputc
单字节读写函数应用
头文件 #include<stdio.h>
函数原型:int fgetc(FILE * stream);
函数参数:FILE * stream 文件流指针, 从哪个文件中读取数据
函数返回值:读到的字符;如果 返回 EOF就说明到文件尾部
函数功能:从文件中读取一个字节数据--读函数--输入---数据 从文件到应用 buf
函数特性:
读操作会引起文件指针的偏移
int ch = fgetc(stdin); 等价于 int ch=getchar();
头文件:#include<stdio.h>
函数原型:int fputc(int c,FILE * stream);
函数参数:
@param1 int c : 要写入的字符
@param2 FILE * stream: 文件流指针,要写到哪个文件中
函数返回值:--应用不多
返回写入成功的字符
函数功能:把一个字符写入文件---写函数--输出---数据 从 应用 buf 到文件
函数特性:
写操作会引起文件指针的偏移
fputc('\n',stdout); 等价于 putchar('\n');
2.3 fgets ,fputs
字符串读写函数应用
头文件:#include<stdio.h>
函数原型:char * fgets(char * s,int size,FILE * stream);
函数参数:
@param1 char * s : 应用层 buf 数组
@param2 int size : 一次读取的字节数
@param3 FILE * stream: 文件流指针--从哪个文件读
函数返回值:
成功则返回 s 指针,
返回 NULL 则表示有错误发生。
函数功能:读函数---输入--数据从 文件 到 buf ,从FILE * stream文件中读取size字节到应用层s所指向的内存中
函数特性:
读操作会引起文件指针的偏移
函数返回值不能作为读取到文件尾部的判断依据,需要借助于其他函数 feof
头文件:#include<stdio.h>
函数原型:int fputs(const char * s,FILE * stream);
函数参数:
@param1 const char * s: 内存
@param2 FILE * stream : 文件
函数返回值:应用不多 ,int 实际写入的字符个数
函数功能:写函数--输出---数据从内存s到文件 把内存s中的所有有效字符 输出到 stream文件中
函数特性:
写操作会引起文件指针的偏移
写入函数无指定 size字节数,是把s内存地址中所有有效字符都写入
---所以在使用fgets ,fputs 最好保证 一次读写的字节数相同
注意事项:
fgets fputs 这对字符串读写 应用不多--使用麻烦
1.fgets函数返回值不能作为文件尾部的判断依据,需要额外引入feof函数
2.fputs函数无指定写入字节数,是把所有有效字符都写入,需要注意应写入的字节个数
2.4 fscanf fprintf
格式化读写函数应用
头文件:#include<stdio.h>
函数原型:
int fscanf(FILE * stream ,const char *format,....);
int scanf( ,const char *format,....);// 相当于文件流指针固定是 stdin
函数参数:
@param1 FILE * stream : 文件流指针--读哪个文件
@param2 const char *format: 格式化控制 %c %d %f %s
.... : 内存地址列表
函数返回值:使用不多 成功则返回参数数目,失败则返回-1,
函数功能:从指定文件中格式化读取数据到内存--取
函数特性:
1.读操作会引起文件指针的偏移
2.要借助于 feof函数对文件尾部判断
int num;float sorce;char buf[20];
scanf("%d %f %s",&num,&sorce,buf); 等价于fscanf(stdin ,"%d %f %s",&num,&sorce,buf);
头文件:#include<stdio.h>
函数原型:int fprintf(FILE * stream, const char * format,.......);
函数参数:
@param1 FILE * stream : 文件流指针--写到哪个文件
@param2 const char *format:格式化控制 %c %d %f %s
.... : 输出列表
函数返回值:使用不多,成功则返回实际输出的字符数目,失败则返回-1,
函数功能:把内存中格式化数据写入到文件--存
函数特性:1.写操作会引起文件指针的偏移
2.5 printf fprintf sprintf 对比
int fprintf(FILE * stream, const char * format,.......); // 格式化输出到指定文件
int printf( ,const char *format,....); // 格式化输出到屏幕
int sprintf(char *buf , const char * format,.......); //格式化输出到char buf中-实际上就是实现 int/float 转换成 char
2.6 fflush feof
头文件:#include<stdio.h>
函数原型:int fflush(FILE* stream);
函数参数:FILE* stream: 文件流指针
函数返回值:成功返回 0,失败返回 EOF
函数功能:刷新缓存区
头文件 #include<stdio.h>
函数原型:int feof(FILE * stream);
函数参数:FILE * stream: 文件流指针--要检测的文件
函数返回值:
==0: 没有到文件尾部
!=0: 已到达文件尾
函数功能:判断是否已经到文件尾部
2.7 fread,fwrite全缓存读写函数
头文件:#include<stdio.h>
函数原型:size_t fread(void * ptr,size_t size,size_t nmemb,FILE * stream);
函数参数:
@param1:void * ptr : 应用层内存buf
@param2: size_t size : 每块的字节数
@param3: size_t nmemb: 读取的块数
@param4 FILE* stream: 文件流指针--从哪个文件中读
一次读取的字节总数= nmemb *size
函数返回值:可以作为文件尾部的判断依据
返回实际读取到的 nmemb 数目
函数功能:读函数--输入--数据从文件到内存buf ,从文件中读取内容到应用内存buf
头文件:#include<stdio.h>
函数原型:size_t fwrite(const void * ptr,size_t size,size_t nmemb,FILE * stream);
函数参数:
@param1 constvoid * ptr : 应用层内存buf
@param2 size_t size : 每块的字节数
@param3 size_t nmemb: 写入的块数
@param4 FILE* stream: 文件流指针--要写到哪个文件
一次读取的字节总数= nmemb *size
函数返回值:使用不多
返回实际写入的 nmemb 数目
函数功能:写函数--输出--数据从内存buf 到文件,把应用内存buf中的数据存入文件
三、练习
1.利用标准io实现fgetc fputc 实现 cp 拷问文件命令的重写
/*利用标准io实现fgetc fputc 实现 cp 拷问文件命令的重写*/
#include <stdio.h>int main(int argc,char *argv[])
{//定义文件流指针FILE*fpsrc,*fpobj;//定义接收用int ch;if(argc!=3){printf("复制格式错误!\n");return -1;}//打开源文件,源文件必须存在fpsrc=fopen(argv[1],"r");if(fpsrc==NULL){printf("源文件打开失败!\n");return -1;}//打开目标文件,目标文件不存在fpobj=fopen(argv[2],"w");if(fpobj==NULL){printf("目标文件打开失败!\n");return -1;}while(1){//从源文件中读内容ch=fgetc(fpsrc);if(ch==EOF)break;//将读到的内容放到目标文件fputc(ch,fpobj);}fclose(fpsrc);fclose(fpobj);return 0;
}
2.利用标准io实现fgets fputs 实现 cat 命令的重写
/*利用标准io实现fgets fputs实现cat命令的重写*/
#include <stdio.h>int main(int argc,char *argv[])
{//定义文件流指针FILE*fpsrc;//定义接收用char recvbuff[20];//定义用于接收返回值char*p=NULL;if(argc!=2){printf("查看格式错误!\n");return -1;}//打开源文件,源文件必须存在fpsrc=fopen(argv[1],"r");if(fpsrc==NULL){printf("源文件打开失败!\n");return -1;}while(1){//从源文件中读内容到数组中p=fgets(recvbuff,20,fpsrc);//当有错误或者读到末尾返回if(p==NULL||feof(fpsrc)==!0)break;//将读到的内容放到目标文件fputs(recvbuff,stdout);}fclose(fpsrc);return 0;
}
3.构造一个学员信息的结构体,包含 姓名 学号 成绩 等成员;编写函数,通过键盘输入3个学员的信息并格把所有信息和学员总数格式化存入文件;---学员总数占一行 每个学生信息占一行然后从文件中读取每个学员的信息并打印
/*构造一个学员信息的结构体,包含 姓名 学号 成绩 等成员;编写函数,通过键盘输入3个学员的信息并格把所有信息和学员总数格式化存入文件;---学员总数占一行 每个学生信息占一行然后从文件中读取每个学员的信息并打印*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct ST
{int num;float socer;char name[20];
}STU;int main()
{STU stu[3];FILE*fp;int num=0;fp=fopen("./1.txt","w+");if(fp==NULL){printf("open error\n");return -1;}//先存入学生总数fprintf(fp,"%d\n",3);//键盘得到信息并存入结构图题中for(int i=0;i<3;i++){printf("请输入第%d个学生的姓名",i+1);scanf("%s",stu[i].name);printf("请输入第%d个学生的学号,成绩",i+1);scanf("%d %f",&stu[i].num,&stu[i].socer);//向1.txt中存入信息fprintf(fp,"%s\t%d\t%f\n",stu[i].name,stu[i].num,stu[i].socer);}//关闭文件fclose(fp);//重新打开文件,使文件指针回到文件开始处fp=fopen("./1.txt","r");if(fp==NULL){printf("open error\n");return -1;}//清空结构体内容memset(stu,0,sizeof(STU)*3);//读取信息fscanf(fp,"%d\n",&num);//将1.txt中的文件输入到numprintf("num=%d\n",num);for(int i=0;i<num;i++){fscanf(fp,"%s\t%d\t%f\n",stu[i].name,&stu[i].num,&stu[i].socer);//将1.txt中的文件输入到结构体printf("姓名%s,学号%d,成绩%f\n",stu[i].name,stu[i].num,stu[i].socer);}return 0;
}