1.什么是标准IO
1.1概念
标准IO:在C库中定义的一组用于输入输出的函数接口
1.2特点
1.通过缓冲机制减少系统调用,提高效率
2.围绕流进行操作,用FILE*来表示
3.标准IO默认打开了三个流,分别是stdin(标准输入)、stdout(标准输出)、stderr(标准错误)
4.一般操作普通文件
2.缓存区
1.全缓存:和文件相关
刷新全缓存的方法:
- 正序正常退出(main中的return,exit函数)
- 缓存区溢出
- 强制刷新(fflush)
- fclose关闭对应的流
2.行缓存
刷新行缓存的方法:
- 正序正常退出(main中的return,exit函数)
- 缓存区溢出
- 强制刷新(fflush)
- fclose关闭对应的流
- \n
3.无缓存:没有缓冲区,标准错误
3.函数接口
3.1 打开文件fopen
man 3 fopen
FILE *fopen(const char *path, const char *mode);
功能:打开文件
参数:
path:打开的文件路径
mode:打开的方式
r:只读,当文件不存在时报错,文件流定位到文件开头
r+:可读可写,当文件不存在时报错,文件流定位到文件开头
w:只写,文件不存在创建,存在则清空
w+:可读可写,文件不存在创建,存在则清空
a:追加(在末尾写),文件不存在创建,存在追加,文件流定位到文件末尾
a+:读和追加,文件不存在创建,存在追加,读文件流定位到文件开头,写文件流定位到文件末尾
注:当a+的方式打开文件时,写只能在末尾进行追加,定位操作是无法改变写的位置,但是可以改变读的位置
返回值:
成功:文件流
失败:NULL,并且会设置错误码
void perror(const char *s);
功能:根据errno值打印对应的错误信息
参数:
s:提示语句
返回值:空
补充:perror()用来将上一个函数发生错误的原因输出到标准设备(stderr)。
参数s所指的字符串会先打印出,后面再加上错误原因字符串。此错误原因依照全局变量errno的值来决定要输出的字符串。
在库函数中有个errno变量,每个errno值对应着以字符串表示的错误类型。当你调用"某些"函数出错时,该函数已经重新设置了errno的值。
perror函数只是将你设置的一些信息和现在的errno所对应的错误一起输出。
3.2 关闭文件 fclose
int fclose(FILE* stream);
功能:关闭文件
参数:stream:文件流
#include <stdio.h>
int main(int argc, char const *argv[])
{
FILE *fp;
//打开文件
//fp = fopen("a.c", "r"); //r: 只读,没有则报错
fp = fopen("b.c", "r");
if (NULL == fp)
{
perror("fopen err"); //如果设成r打开方式则没有这个文件会报错,因为只读方式打开
return -1;
}
printf("fopen success\n");
//关闭文件
fclose(fp);
return 0;
}
3.3 文件读写操作:fgets、fputs、fread、fwrite
3.3.1 每次读写一串字符fgets()和fputs()
char * fgets(char *s, int size, FILE * stream);
功能:从文件中每次读取一行字符串
参数: s:存放字符串的地址
size:期望一次读取的字符个数
stream:文件流
返回值:成功:s的地址
失败或读到文件末尾:NULL
特性: 每次实际读取的字符个数为size-1个,会在末尾自动添加\0
每次读一行,遇到\n或者到达文件末尾后不再继续读下一行
并把它存储在s所指向的字符串内。
int fputs(const char *s, FILE * stream);
功能:向文件中写一个字符串
参数:s:要写的内容
stream:文件流
返回值:成功:非负整数
失败:EOF
针对终端
#include <stdio.h>
int main(int argc, char const *argv[])
{
char buf[32] = "";
//针对终端读写
//输入操作
fgets(buf, sizeof(buf), stdin); //终端输入的内容是hello\n
printf("buf: %s\n", buf); //此时buf里面存入的内容是: hello\n\0
//输出操作
fputs(buf, stdout); //输出buf内容到终端: hello\n\0
fputs("world",stdout); //输出world\0到终端
return 0;
}
注意: fgets一定会留一个位置给\0
针对文件
文件内容:
#include <stdio.h>
int main(int argc, char const *argv[])
{
FILE *fp;
//打开文件
fp = fopen("test.txt", "r+");
if (NULL == fp)
{
perror("fopen err");
return -1;
}
//读写操作
char buf[32] = "";
fgets(buf, 32, fp); //buf:hello\n\0
fputs(buf, stdout);
fgets(buf, 32, fp); //buf:world\n\0
fputs(buf, stdout);
fgets(buf, 32, fp); //buf:6\0rld\n\0
fputs(buf, stdout);
//关闭文件
fclose(fp);
return 0;
}
3.3.2 二进制读写fread()和fwrite()
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
功能:从文件流读取多个元素(将二进制数据从文件读出)
参数: ptr :是一个指针,是存放数据的存储空间的起始地址,用来存放读取元素
size :元素大小 sizeof(元素数据类型)
nmemb :读取元素的个数
stream :要读取的文件流
返回值:成功:读取的元素的个数
读到文件尾或失败: 0
size_t fwrite(const void *ptr, size_t size, size_t nmemb,
FILE *stream);
功能:将二进制数据写入文件
参数: ptr :是一个指针,保存要输出数据的空间的地址。
size :要写入的字节数 sizeof(数据类型)
nmemb : 要进行写入元素的个数
stream: 目标文件流指针
返回值:成功:写的元素个数
失败 :-1
#include <stdio.h>
int main(int argc, char const *argv[])
{
float arr[3] = {1.2, 2.3, 3.4};
float data[3] = {0};
//打开文件
FILE *fp = fopen(argv[1], "r+");
if (fp == NULL) //容错判断
{
perror("fopen err\n");
return -1;
}
printf("fopen okk\n");
fwrite(arr, 4, 3, fp);
rewind(fp); //将流中的位置定位到开头, 不然上一步写操作位置到末尾了如果接着读的话什么也读不到
fread(data, 4, 3, fp);
for (int i = 0; i < 3; i++)
printf("%f\n", data[i]);
fclose(fp);
return 0;
}
3.4 文件定位操作
void rewind(FILE *stream);
功能:将文件位置指针定位到起始位置
int fseek(FILE *stream, long offset, int whence);
功能:文件的定位操作
参数:stream:文件流
offset:偏移量:正数表示向后文件尾部偏移,负数表示向文件开头偏移
whence:相对位置:
SEEK_SET:相对于文件开头
SEEK_CUR:相对于文件当前位置
SEEK_END:相对于文件末尾
返回值:成功:0
失败:-1
注:当打开文件的方式为a或a+时,fseek不起作用
补充:其中SEEK_SET,SEEK_CUR和SEEK_END和依次为0,1和2.
例子:
把fp指针移动到离文件开头100字节处:fseek(fp,100,0);
把fp指针移动到离文件当前位置100字节处: fseek(fp,100,1);
把fp指针退回到离文件结尾100字节处: fseek(fp,-100,2);
long ftell(FILE *stream);
功能:获取当前的文件位置
参数:要检测的文件流
返回值:
成功:当前的文件位置,出错:-1
#include <stdio.h>
int main(int argc, char const *argv[])
{
//打开文件
FILE *fp = fopen("test.txt", "w+");
if (fp == NULL) //容错判断
{
perror("fopen err\n");
return -1;
}
printf("fopen okk\n");
//相当于开头位置往后定位10个
fseek(fp, 10, 0);
fputs("a", fp);
//相当于当前位置往后5个
fseek(fp, 5, 1);
fputs("hello", fp);
//相当于最后位置往前1个
fseek(fp, -1, 2);
fputs("i", fp);
long l = ftell(fp);
printf("%ld\n", l);
fclose(fp);
return 0;
}
总结:为什么用标准IO?
- 因为读写文件通常是大量的数据(相对于底层驱动的系统调用所实现的数据操作单位),这时,使用库函数可以大大减少系统调用的次数。
- 为了保证可移植性