二、标准IO
2.1 标准IO的定义
标准IO是由标准C库提供的API接口,是在文件IO的基础上封装出来的函数接口,第一个原因是为了增加可移植性。并且,标准IO在文件IO的基础上封装了一个叫缓冲区的东西,用来存放一些不是很着急的数据,等到缓冲区满,调用一次文件IO完成写入工作或者读取工作。有些数据是特别着急的,
比如:下发的命令,实时监控数据,必须要使用文件IO。 伪代码: fopen(参数) { if(is win) { _open(参数); } else if(is linux) { open(参数); } }
2.1.1 文件流指针
FILE pointer
文件流指针:是一个文件的标志,是在文件描述符上封装出来的一个结构体指针,缓冲区就在这个流指针中。
流:流指针 -----文件流指针 —这个流
追代码:
安装一个索引搜索文件tags(搜索引擎)
第一步:创建目录的引擎文件—tags (想要搜索哪个目录就在哪个目录下创建)
ctags -R //生成一个引擎文件叫tags
第二步:搜索
vi -t 目标字符串 例如: vi -t FILE
第三步:选择准确的目录下的文件
第四步:翻阅文档----》代码跳转
追代码:光标点击想要跳转的字符串 ,ctrl + ]进行跳转搜索
返回上一层:ctrl + t
2.2 标准IO函数接口
2.2.1 fopen
头文件: #include
原型:FILE *fopen(const char *pathname, const char *mode);
功能:打开一个文件
参数:pathname:要打开文件的路径及名称
mode:打开文件的方式
r:以只读的方式打开一个文件,文件必须存在
r+:以读写的方式打开一个文件,文件必须存在
w:如果文件存在则清空写入,如果文件不存在则创建写入
w+:如果文件存在则清空读写,如果文件不存在则创建读写
a:如果文件存在则追加写入,如果文件不存在则创建再写入
a+:如果文件不存在则创建文件并追加写入和读取,读取时从文件开头开始读取,写入时从文件的末尾开始写入,lseek偏移后不能进行写入操作
如果进行写入操作,则追加在末尾写入(不管之前如何偏移)
注意:有可能以后会遇到 rb+,b代表执行二进制操作,linux下无区分
返回值:
成功会返回一个文件流指针
失败返回NULL。
#include <stdio.h> int main(int argc, char const *argv[]) { //打开一个文件需要定义一个文件流指针来指向fopen返回的已经封装好的文件流指针 //FILE *fp = fopen("./1.txt","r"); FILE *fp = fopen("./1.txt","w"); if(NULL == fp) { perror("open"); return -1; } printf("打开文件成功!\n"); return 0; }
2.2.2 fclose
头文件:
#include
原型:
int fclose(FILE *stream);
功能:关闭一个文件流指针 //文件流指针里也绑定了一个文件的描述符
参数:目标文件流指针
返回值:
成功:返回 0
失败: -1
2.2.3. fgetc
头文件:
#include
原型:
int fgetc(FILE *stream);
功能:从指定文件流指针中获取一个字符
参数:目标文件流指针
返回值:
成功:返回获取到的字符转化成int类型的数据
失败: 返回EOF
到达文件末尾: EOF
2.2.4 feof
头文件:
#include
原型:
int feof(FILE *stream);
功能:判断是否到达了文件末尾
参数:目标文件流指针
返回值:如果到达了文件的末尾返回一个非0值
其它情况返回0
2.2.5 ferror
头文件:
#include
原型:
int ferror(FILE *stream);
功能:判断文件操作是否出错
参数:目标文件流指针
返回值:如果文件操作失败了,返回一个非0值
其它情况返回0
#include <stdio.h> int main(int argc, char const *argv[]) { FILE *fp = fopen("./1.txt","r"); if(NULL == fp) { perror("fopen"); return -1; } //开始做循环读取 while(1) { int ret = fgetc(fp); if(EOF == ret) { if(feof(fp)) { printf("到达了文件末尾!\n"); break; } else { printf("获取失败!\n"); return -1; } } printf("获取到的数据为%d---所对应的字符为%c\n",ret,ret); } if(fclose(fp) == EOF) { printf("关闭失败\n"); return -1; } return 0; }
2.2.6 fputc
头文件:
#include
原型:
int fputc(int c, FILE *stream);
功能:向指定的一个文件输出一个字符
参数:c:想要写入的字符,将int转换成 unsigned char类型的数据写入
stream:目标文件流指针
返回值:
成功返回:写入的字符转换成int类型的数据
失败返回:EOF
#include <stdio.h> #include <string.h> int main(int argc, char const *argv[]) { FILE *fp = fopen("./1.txt","w"); if(NULL == fp) { perror("fopen"); return -1; } //开始向文件中写入数据 char buf[123] = "hello world"; int i = 0; while(1) { fputc(buf[i],fp); if((strlen(buf) - 1) == i) { break; } i++; //遍历数组使用 } fclose(fp); return 0; }
2.2.7 fgets
头文件:
#include
原型:
char *fgets(char *s, int size, FILE *stream);
功能:行读取
参数:s:读取到的数据存放的地址
size:读取的字节个数
1.当\n之前的字节个数小于size,遇到\n结束读取
但是换行符也会被当做一个字符被读到内存之中,并且会在最后一个字符后添加’\0’.
2.当\n之前的字节个数大于size,会读到size个大小的字节时停止读取,
会在末尾加上一个’\0’,会将最后一个字节的字符给覆盖掉。
机制会在读取完之后读写指针向前偏移一个字节
stream:目标文件流指针
返回值:
成功返回:读到的字符串的首地址
失败返回:NULL
读到文件末尾:返回NULL
#include <stdio.h> int main(int argc, char const *argv[]) { FILE *fp = fopen("./1.txt","r"); if(NULL == fp) { perror("fopen"); return -1; } //行读取 while(1) { char buf[123] = {0}; if(fgets(buf,11,fp) == NULL) { //失败或者读到文件末尾 if(feof(fp)) { printf("读到了文件的末尾\n"); break; } else { perror("fgets"); return -1; } } printf("buf = %s\n",buf); } fclose(fp); return 0; }
2.2.8 fputs
头文件:
#include
原型:
int fputs(const char *s, FILE *stream);
功能:向一个文件进行字符串输入
参数:s:想要写入的数据的地址
stream:目标文件流指针
返回值:
成功返回:一个非负数
失败返回:EOF(-1)
#include <stdio.h> int main(int argc, char const *argv[]) { FILE *fp = fopen("./1.txt","w"); if(NULL == fp) { perror("fopen"); return -1; } //想要写入的数据的地址 char buf[123] = "hello world"; if(EOF == fputs(buf,fp)) { perror("fputs"); return -1; } fclose(fp); return 0; }
练习:请编写一个代码,用来测试文件的行数
#include <stdio.h> #include <string.h> int main(int argc, char const *argv[]) { FILE *fp = fopen(argv[1],"r"); if(NULL == fp) { perror("fopen"); return -1; } int count = 0; //开始计算 while(1) { char buf[123] = {0}; if(NULL == fgets(buf,sizeof(buf),fp)) { //出错或者到了文件末尾 if(feof(fp)) { printf("到达了文件的末尾\n"); break; } else { perror("fgets"); return -1; } } if(buf[strlen(buf) - 1] == '\n') { printf("buf = %s\n",buf); count++; } } count++; printf("该文件的行数为:%d\n",count); fclose(fp); return 0; }
2.2.9 fread
头文件:
#include
原型:
size_t fread(void *ptr, size_t size, size_t nmemb, FILE stream);
功能:二进制读取文件
参数:ptr:存放读到的数据的地址
size:对象的大小
nmemb:对象的个数
注意:总的读取的字节个数为对象的大小对象的个数
stream:目标文件流指针
返回值:
成功返回:成功返回读到的对象的个数
失败返回:0
2.2.10 fwrite
头文件:
#include
原型:
size_t fwrite(const void *ptr, size_t size, size_t nmemb,
FILE *stream);
功能:二进制写入文件
参数:ptr:想要写入的数据的首地址
size:对象的大小
nmemb:对象的个数
stream:目标文件流指针
返回值:
成功返回:成功返回读到的对象的大小
失败返回:返回一个小的对象的个数
#include <stdio.h> #include <string.h> struct person { char name[32]; char sex; int age; char phone[12]; }; struct person p1; //定义一个录入使用的结构体 struct person p2; //定义一个读取时使用的结构体 int main(int argc, char const *argv[]) { STAT: //界面: printf("****************************\n"); printf("1.录入信息 2 读取信息 3.退出\n"); printf("****************************\n"); printf("input>> "); int input = 0; scanf("%d",&input); if (input > 3 || input < 0) { goto STAT; } if(1 == input) { printf("请输入姓名\n"); scanf("%s",p1.name); getchar(); printf("请输入性别\n"); scanf("%c",&p1.sex); getchar(); printf("请输入年龄\n"); scanf("%d",&p1.age); printf("请输入手机号\n"); scanf("%s",p1.phone); getchar(); FILE *fp = fopen("./1.txt","a"); if(NULL == fp) { perror("fopen"); return -1; } if(1 != fwrite(&p1,sizeof(p1),1,fp)) { perror("fwrite"); return -1; } fclose(fp); printf("写入信息结束,正在跳转菜单"); goto STAT; } else if(2 == input) { char usrname[32] = {0}; //读取信息 printf("请输入要查询人的姓名\n"); scanf("%s",usrname); getchar(); //开始对文件进行读取并匹配 FILE *fp = fopen("./1.txt","r"); if(NULL == fp) { perror("fopen"); return -1; } //循环读取 while(1) { if(0 == fread(&p2,sizeof(p2),1,fp)) { if(feof(fp)) { printf("读到文件末尾\n"); break; } else { perror("fread"); return -1; } } if(strcmp(usrname,p2.name) == 0) { printf("姓名:%s\n",p2.name); printf("性别:%c\n",p2.sex); printf("年龄:%d\n",p2.age); printf("手机号:%s\n",p2.phone); } } } return 0; }