3.函数接口
3.1打开文件fopen
FILE *fopen(const char *path, const char *mode);
功能:打开文件
参数:
path:打开的文件路径
mode:打开的方式
r:只读,当文件不存在时报错,文件流定位到文件开头
r+:可读可写,当文件不存在时报错,文件流定位到文件开头
w:只写,文件不存在创建,存在则清空
w+:可读可写,文件不存在创建,存在则清空
a:追加(在末尾写),文件不存在创建,存在追加,文件流定位到文件末尾
a+:读和追加,文件不存在创建,存在追加,读文件流定位到文件开头,写文件流定位到文件末尾
注:当a+的方式打开文件时,写只能在末尾进行追加,定位操作是无法改变写的位置,但是可以改变读的位置
返回值:
成功:文件流
失败:NULL,并且会设置错误码
补充:
perror ( )用 来 将 上 一 个 函 数 发 生 错 误 的 原 因 输 出 到 标 准 设备 (stderr) 。参数 s 所指的字符串会先打印出, 后面再加上错误原因字符串。此错误原因依照全局变量errno 的值来决定要输出的字符串。 在库函数中有个errno变量,每个errno值对应着以字符串表示的错误类型。当你调用"某些"函数出错时,该函数已经重新设置了errno的值。perror函数只是将你设置的一些信息和现在的errno所对应的错误一起输出。
3.2关闭文件
fclose()
功能:关闭文件
参数:文件流
#include <stdio.h>
int main(int argc, char const *argv[])
{
FILE *fp;
//1.打开文件
//fp = fopen("test.txt", "r"); //如果不存在会错误,因为只读r模式
fp = fopen("test.txt", "w");
if (NULL == fp)
{
perror("fopen err");
// while(1); //标准错误不缓存,错误信息会打印
return -1;
}
printf("fopen success\n");
//2.关闭文件
fclose(fp);
return 0;
}
3.3文件读写操作
3.3.1每次读一个字符:fgetc(),fputc()
每次读一个字符fgetc()
fgetc()
功能:从文件中读取一个字符,并且将当前文件指针位置向后移动一个字符
参数:文件流
返回值:
成功:读到的字符
失败或者读到文件末尾:EOF(-1)
每次写入一个字符fputc()
fputc()
功能:向文件写入一个字符,成功写入后文件指针自动向后移动一个字节位置
参数:要写的字符 文件流
返回值
成功,写入的字符的ascii
失败EOF(-1)
(1)针对文件读写
#include <stdio.h>
int main(int argc, char const *argv[])
{
FILE *fp;
//1.打开文件
fp = fopen("test.txt", "r+");
if (NULL == fp)
{
perror("fopen err");
return -1;
}
printf("fopen success\n");
char ch = fgetc(fp);
printf("%d %c\n", ch, ch); //104 h
ch = fgetc(fp);
printf("%d %c\n", ch, ch); //101 e
fputc('a', fp); //向文件中写个a
fputc('b', fp); //向文件中写个b
ch = fgetc(fp);
printf("%d %c\n", ch, ch); //-1 EOF 因为以及到末尾了
//2.关闭文件
fclose(fp);
return 0;
}
(2)针对终端的读写
#include <stdio.h>
int main(int argc, char const *argv[])
{
char ch;
ch = fgetc(stdin); //相当于getchar()
printf("%d %c\n", ch, ch);
fputc(ch, stdout); //相当于putchar()
fputc(10, stdout);
fputc('b', stdout);
return 0;
}
补充feof和ferror
int feof(FILE * stream);
功能:判断文件有没有到结尾,也就是当前所在位置后面还有没有字符。
返回:如果到达文件末尾,返回非零值。如果后面还有字符则返回0。
例子:
int ferror(FILE * stream);
功能:检测文件有没有出错
返回:文件出错,返回非零值。如果没有出错,返回0。
练习;cat文件名
#include <stdio.h>
/* cat 文件名:查看文件内容,显示到终端。
步骤:
1.打开文件 fopen
2.循环用fgetc获取文件内容
3.当读到文件末尾标志EOF时结束
4.将读取文件内容用fputc打印到终端
5.关闭文件
*/
int main(int argc, char const *argv[])
{
if (argc != 2)
{
printf("err: %s <filename>\n", argv[0]); //提示一下正确的格式
return -1;
}
//1.打开文件
FILE *fp = fopen(argv[1], "r");
if (NULL == fp)
{
perror("fopen err");
return -1;
}
//2.循环用fgetc读文件,只要读到就fputc打印到终端
char ch;
while ((ch = fgetc(fp)) != EOF)
fputc(ch, stdout); //将读到字符打印到终端
//3. 关闭文件
fclose(fp);
return 0;
}
3.3.2每次一个字符串的读写fgets()和fputs()
fgets()
功能:从文件中每一次读取一个字符串
参数:
存放字符串的地址
期望一次读取的字符个数
文件流
返回值
成功,存发给字符串的地址
失败或者读到文件末尾:NULL
fputs()
功能:向文件中写入字符串
参数:
要写的内容
文件流
返回值:
成功,非负整数
失败EOF
(1)针对终端的读写
#include <stdio.h>
int main(int argc, char const *argv[])
{
char buf[32] = "";
//输入操作
fgets(buf, 32, stdin); //输入hello\n, 此时buf中存入内容: hello\n\0
printf("buf: %s\n", buf);
fgets(buf, 32, stdin); //输入66\n, 此时buf中内容: 66\n\0o\n\0
printf("buf: %s\n", buf);
//输出操作
fputs("world", stdout);
fputs(buf, stdout);
return 0;
}
(2)针对文件的读写
#include <stdio.h>
int main(int argc, char const *argv[])
{
FILE *fp;
char buf[32] = "";
fp = fopen("test.txt", "r+");
if (NULL == fp)
{
perror("fopen err");
return -1;
}
printf("fopen success\n");
//读写操作
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: 66\0ld\n\0
fputs(buf, stdout);
fclose(fp);
return 0;
}
通过fgets实现wc -l文件名实现计算文件行数的命令功能
#include <stdio.h>
#include <string.h>
int main(int argc, char const *argv[])
{
FILE *fp;
char buf[32] = "";
if (argc != 2)
{
printf("err: %s <filenme>\n",argv[0]);
return -1;
}
fp = fopen(argv[1], "r");
if (NULL == fp)
{
perror("fopen err");
return -1;
}
//循环fgets读文件, 只要读到就判断是否有\n, 如果有就累加行数
int len = 0;
while (fgets(buf, 32, fp) != NULL)
{
if (buf[strlen(buf) - 1] == '\n')
len++;
}
printf("%d %s\n", len, argv[1]); //wc -l会少一行,因为最后一行没有换行
fclose(fp);
return 0;
}
3.3.3二进制读写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 : 要进行写入元素的个数
strem: 目标文件流指针
返回值:成功:写的元素个数
失败 :-1
(1)针对终端的读写
//针对终端
char buf[32] = "";
fread(buf, sizeof(char), 10, stdin);
printf("printf:%s\n", buf);
fwrite(buf, 1, 10, stdout);
针对文件的读写
#include <stdio.h>
int main(int argc, char const *argv[])
{
FILE *fp;
float arr[3] = {1.23, 2.34, 4.56};
float data[3] = {0};
fp = fopen(argv[1], "r+");
if (fp == NULL)
{
perror("fopen err");
return -1;
}
fwrite(arr, sizeof(float), 3, fp);
//将位置指针定位到文件开头,不然上一步是写操作所以接着读的话会从末尾开始读从而什么都读不到
rewind(fp); //定位到文件开头
fread(data, 4, 3, fp);
printf("%f %f %f\n", data[0], data[1], data[2]);
return 0;
}
3.4其他操作
3.4.1重定向流到文件freopen
ILE * freopen(const char *pathname, const char *mode, FILE* fp);
功能:将指定的文件流重定向到打开的文件中
参数:path:文件路径
mode:打开文件的方式(同fopen)
fp:文件流指针
返回值:成功:返回打开的文件流指针
失败:NULL
#include <stdio.h>
int main(int argc, char const *argv[])
{
printf("hello\n");
//将标准输出流重定向到打开的test.txt文件中
freopen("test.txt", "w+", stdout);
printf("world\n");
//将标准输出重定向到终端文件
freopen("/dev/tty", "r+", stdout);
printf("6666666666666666\n");
return 0;
}
3.4.2文件定位操作
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.
简言之:
fseek(fp,100,0);把fp指针移动到离文件开头100字节处.
fseek(fp,100,1);把fp指针移动到离文件当前位置100字节处;
fseek(fp,-100,2);把fp指针退回到离文件结尾100字节处。
long ftell(FILE *stream);
功能:获取当前的文件位置
参数:要检测的文件流
返回值:成功:当前的文件位置,出错:-1
#include <stdio.h>
int main(int argc, char const *argv[])
{
FILE *fp = fopen("test.txt", "w+");
if (NULL == fp)
{
perror("fopen err");
return -1;
}
printf("fopen success\n");
//相当于开头位置,往后10个
fseek(fp, 10, 0);
fputc('a', fp);
//相对于当前位置,往后5个
fseek(fp, 5, 1);
fputs("hello", fp);
//相对于最后位置,往前1个
fseek(fp, -1, 2);
fputc('b', fp);
//计算文件位置
long l = ftell(fp);
printf("%ld\n", l);
return 0;
}
总结
为什么要用标准io
1.因为读写文件通常是大量的数据,库函数可以大量减少系统调用的次数
2.为了保持可移植性
关于缓存区
库函数的缓冲区对于库函数,如果标准输出连到终端设备(直接输出到屏幕),则它是行缓冲的(遇到回车换行符或者是缓冲区满了才输出);否则(输出到文件)是全缓冲的(缓冲区填满或者是程序运行结束了才输出)。程序运行结束时,会刷新所有的缓冲区。