什么是IO
- I:input ——输入,从外部存储设备将数据输入到内存中
- O:output ——输出,数据从内存到外部存储设备中
- 输入输出就是数据从外存到内存,内存到外存的流向
IO分类
文件IO函数
- 文件IO是由操作系统提供的基本IO函数,与操作系统绑定,也可以称为操作系统的调用
- 移植性低,只能运行在对应的操作系统中
- 文件IO涉及到用户空间到内核空间的切换、cpu模式的切换、C代码调用汇编指令等等,属于一种耗时操作,应该尽量减少文件IO函数的使用
- 若要操作内核,内核只能使用文件IO函数
标准IO函数
- 标准IO函数根据ANSI标准,是对文件IO函数的二次封装
- 最终标准IO函数依然会去调用文件IO函数
- 提高了代码的可移植性和复用性
- 提高输入输出效率
在用户空间设置一个缓冲区,缓冲区满或者满足一定条件之后,调用文件IO函数。
标准IO函数
流和流指针
- 流:流字节,将数据一个一个的移入或移出缓冲区的形式叫做字节流
- (FILE *)流指针:当要做IO操作时,需要打开一个对应文件,每打开一个文件就会在内存中申请一片缓冲区,同时管理和维护这片空间的变量都存储在FILE结构体中,FILE结构体由系统定义好,可直接调用。
FILE结构体成员
- 查看指令:vi -t(这个指令可以查看系统定义好的 数据类型、宏、变量)
- 格式:vi -t FILE
- 追加代码:
1>将光标停留在要追的字符上, 按下ctrl + ]
2>ctrl + 鼠标左键点击要追的内容
- 返回:
1>ctrl+t;
2>ctrl+鼠标右键
标准IO函数
fopen
- 功能:打开一个文件
- 原型:FILE *fopen(const char *pathname, const char *mode);
- 参数:char *pathname:指定要打开的文件路径以及名字
char *mode:以什么方式打
(1)r:以读的方式打开文件,流在文件开头位置(从开头开始读取),若不存在,则打开失败
(2)r+:以读写的方式打开文件,流在文件开头位置,若不存在,则打开失败
(3)w:以写的方式打开文件,若要写文件,则从开头开始;若文件不存在,则创建并打开;若文件存在,则会清空原文件
(4)w+:以读写的方式打开文件,若要写文件,则从开头开始;若文件不存在,则创建并打开;若文件存在,则会清空原文件
(5) a:以写的方式打开文件,不会清空文件,流在文件结尾,若文件不存在,则会创建文件并打开
(6) a+:以读写的方式打开文件,若文件不存在,则会创建文件并打开;若文件存在,开始为读,则流在文件开头,开始为写,则流在文件结尾
- 返回值:成功返回FILE *指针类型,失败返回NULL,同时更新error
errno:本质上是一个整型数,不同的错误会更新不同的errno;
perror
- 功能:根据errno,打印对应的错误信息
- 原型:void perror(const char *s)
- 参数:char *s:用于提示的字符串
fclose
- 功能:关闭指定的文件,释放资源
- 原型: int fclose(FILE *stream)
- 参数:FILE *stream:指定要关闭的文件对应的流指针
- 返回值:成功返回0,失败返回EOF,同时更新error
fprintf
- 功能:将数据格式化输出到指定文件中
- 原型: int printf(const char *format, …)
int fprintf(FILE *stream, const char *format, …)
- FILE *stream:流指针,指定要输出到哪个文件中,就填对应文件的流指针
char *format:格式化字符串:字符,占位符,转义字符
…:不定参数,不定数据个数,不定数据类型
- 返回值:成功:返回被打印的字符个数
失败:返回负数
fscanf
- 功能:从指定文件中格式化读取数据(%d %s不识别空格和转义字符,%c识别空格和转义字符)
- 原型: int scanf(const char *format, …);
int fscanf(FILE *stream, const char *format, …);
参数:FILE *stream:流指针,指定要输出到哪个文件中,就填对应文件的流指针
char *format:格式化字符串:字符,占位符,转义字符
…:不定参数,不定数据个数,不定数据类型
- 返回值:成功:返回成功读取的数据个数
==EFO表示文件读取完毕也表示运行失败,同时更新errno
fputc
- 功能:将单个字符打印到指定的文件中
- 原型: int fputc(int c, FILE *stream);
- 参数:int c:指定要输出的字符对应的字符形式或者整型形式
FILE *stream:流指针,对应的文件的流指针
- 返回值:成功返回输出字符对应的整型形式;失败返回EOF
fgetc
- 功能:从指定文件中读取单个字符
- 原型:int fgetc(FILE *stream)
- 参数:FILE *stream:指定要从哪个文件中读取
- 返回值:返回成功读取到字符对应的整型形式;当文件读取完毕或者函数运行失败,返回EOF
注意:
- %s %d不识别空格 \n \t,若想要获取到上述字符,需要使用%c形式
- 读写操作后,文件偏移量会自动偏移,所以若想要从头读取数据,就需要想办法将偏移量修改到开头位置
作业
一、实现登录功能。自定义一个usr.txt,手动输入账户密码,格式如下:账户 密码
例如: zhangsan 12345
lisi abcde
wangwu abc123
需求如下:
从终端获取账户密码,与文件中的账户密码比较
若终端输入的账户不存在,则输出账户不存在
若终端输入的账户存在,但是密码不正确,则输出密码错误
若账户密码均正确,则输出登录成功
附加题
实现注册功能,注册的账户密码存储在上一题的文件中。需求:不能重复注册。
#include
#include
#include
int main(int argc, const char *argv[])
{
FILE *fp=fopen("usr.txt","a+");
if(NULL==fp)
{
printf("打开失败\n");
return -1;
}
char num[20]="";
char password[20]="";
printf("请输入账号:");
scanf("%s",num);
printf("请输入密码:");
scanf("%s",password);
char name_u[20]="";
char password_u[20]="";
int c=0;
char ch;
while(1)
{
c=fscanf(fp,"%s %s",name_u,password_u);
if(EOF==c)
break;
if(0 == strcmp(name_u,num) && 0 == strcmp(password,password_u))
{
printf("登录成功\n");
break;
}
else if(0 != strcmp(name_u,num))
{
printf("用户名不存在,是否要注册,请输入Y/N:");
scanf(" %c",&ch);
if('N'==ch)
{
break;
}
else
{
fprintf(fp,"%s %s\n",num,password);
printf("注册成功\n");
}
}
else if(0 == strcmp(name_u,num) || 0 != strcmp(password,password_u))
{
printf("密码错误\n");
break;
}
}
fclose(fp);
fp=NULL;
return 0;
}
二、用fgetc与fputc函数实现:运算符优先级: =号优先级最低。
文件拷贝,例如将1.txt的内容拷贝到2.txt中
要求用fgetc计算一个文件有多少个字节
用fgetc计算一个文件有几行?
#include
#include
#include
int main(int argc, const char *argv[])
{
FILE *fp_r=fopen("1.txt","r");
FILE *fp_w=fopen("2.txt","w+");
if(NULL == fp_r || NULL == fp_w)
{
perror("fopen");
return -1;
}
int c=0;
int count=0;
int line=0;
while(1)
{
c=fgetc(fp_r);
if('\n'== c)
line++;
if(EOF == c)
break;
count++;
fputc(c,fp_w);
printf("%c",c);
}
printf("fp_r文件有%d个字节,有%d行\n",count,line);
fclose(fp_r);
fclose(fp_w);
fp_r=fp_w=NULL;
return 0;
}
三、思维导图总结