前言
本文记录标准文件IO,即标准C库提供的针对文件打开读写操作的函数。
文件IO
文件的读写操作就叫文件IO
文件IO分为两类:
(1)标准文件IO 就是标准C库提供的针对文件打开读写操作的函数;
(2)系统文件IO 就是Linux系统提供答完针对文件打开读写操作的函数。
标准文件IO和Linux文件IO区别
(1)标准文件IO存在缓存区,Linux文件IO不存在缓存区;
(2)标准文件IO只能打开普通文件,Linux文件IO可以打开不同类型的文件(管道文件、套接字文件);
(3)标准文件IO用FILE*文件流指针来识别每个文件,Linux文件IO用in fd文件描述符来识别每个文件;
(4)两者的提供函数不同。
标准文件IO
标准文件IO存在文件缓冲区,减少系统开销。(重点)
全缓存
需要经过缓存区,缓存区(默认1024字节)满了一次性打印在终端上。
int main()
{
while(1)
{
printf("hello world"); //此处不加\n
sleep(1);
}
return 0;
}
行缓存
加上\n不经过缓存区,直接将内容输出到终端上
int main()
{
while(1)
{
printf("hello world\n"); //此处加\n
sleep(1);
}
return 0;
}
标准文件IO相关函数
fopen函数
#include <stdio.h>
FILE *fopen(const char *pathname, const char *mode);
//调用参考
FILE* fp = fopen("./hello.txt","r"); //绝对路径,以只读方式打开当前路径下的hello.txt
功能:打开文件
参数1:打开文件的路径,可以是相对路径或者绝对路径
参数2:打开文件的方式
"r" | 只读的方式打开文件 |
"r+" | 可读可写的方式打开文件 |
"w" | 只写的方式打开文件 如果文件不存在,创建文件以只写方式打开; 如果文件存在,清空再以只写方式打开。 |
"w+" | 可读可写的方式打开文件 如果文件不存在,创建文件以可读可写方式打开; 如果文件存在,清空再以可读可写方式打开。 |
"a" | 文件末尾写追加信息的方式打开 文件不存在,创建文件以末尾追加方式打开; 文件存在,不清空追加写方式打开。 |
"a+" | 以可读末尾写追加的方式打开 |
返回值:成功,返回FILE*(打开的文件在内核中的首地址);失败,返回NULL。
FILE*指针,文件流指针,FILE是一个结构体,每打开一个文件,内核就会为这个文件开辟内存空间来保存打开文件的相关内容。
fgetc函数
#include <stdio.h>
int fgetc(FILE *stream);
//调用参考
char ch = fgetc(fp);
功能:每次读取一个字符
参数:文件流指针,fopen函数的返回值
返回值:成功,返回对应的ASCII码值;
失败,返回EOF(读取到文件末尾时返回EOF,循环读取时,可作为结束标识) #define EOF -1
fputc函数
#include <stdio.h>
int fputc(int c, FILE *stream);
功能:每次写入一个字符
参数1:写入的字符
参数2:文件流指针,fopen函数的返回值
返回值: 成功,返回写入字符;失败,返回-1。
fclose函数
关闭打开的文件之前刷新缓存区,才会把读写内容写入到文件中。之后再关闭释放空间。
int fclose(FILE *fp);
perror函数
#include <stdio.h>
void perror(const char *s);
功能:打印错误原因
参数:打印错误原因前的提示信息
strerror函数
#include <string.h>
char *strerror(int errnum);
//调用参考
printf("fopen failed:%s\n",strerror(errno));
功能:打印错误提示信息,但是需要错误信息编号作为参数。
Linux系统下有一个int类型的全局变量errno,即错误信息的编号
参数:错误编号
返回值:返回错误原因字符串的首地址。
fgets函数
读取到文件末尾的标识是NULL;当buf数组长度不够存放读取的一行内容时,最多读取sizeof(buf)-1,最后一位存放'\0'。字符串必须以\0结尾。
#include <stdio.h>
char *fgets(char *s, int size, FILE *stream);
功能:每次读取一行(以\n作为读取的终止符)
参数1:读取出来的一行内容
参数2:一次最多读取的字节数
参数3:文件流指针,fopen函数的返回值
返回值:成功,读取到内容的首地址;失败,返回NULL。
案例
#include <stdio.h>
int main()
{
char buf[100] = {0};
//以可读的模式打开文件
FILE *fp = fopen("./fgets.c","r");
if(NULL == fp)
{
//使用perror函数打印出具体的错误提示信息
perror("fopen failed reason");
return -1;
}
//循环读取文件中的所有内容
while(fgets(buf,sizeof(buf),fp) != NULL)
{
printf("%s",buf);
}
//关闭文件
fclose(fp);
return 0;
}
fputs函数
#include <stdio.h>
int fputs(const char *s, FILE *stream);
功能: 一次写入一行
参数1:存放要写入的字符串首地址
参数2:文件流指针,fopen函数的返回值
返回值:成功,返回实际写入的字符数;失败,返回EOF
案例
#include <stdio.h>
int main()
{
char buf[100] = {0};
FILE *fp = fopen("./fgets.c","r");
FILE *fp1 = fopen("./demo.c","w");
if(NULL == fp||NULL == fp1)
{
perror("fopen failed reason");
return -1;
}
//循环读取文件中的所有内容
while(fgets(buf,sizeof(buf),fp) != NULL)
{
fputs(buf,fp1);
}
fclose(fp);
fclose(fp1);
return 0;
}
fflush函数
手动刷新缓存区,防止中断结束内容未写到文件中,实现写入一次,成功一次。
//for循环输入三个名字,保存到name.txt中
#include <stdio.h>
#include <string.h>
int main()
{
char buf[128]; //保存要写入的字符串
FILE *fp = fopen("./name.txt","w");
if(NULL == fp)
{
perror("fopen failed");
return -1;
}
for(int i = 0;i < 3;i++)
{
scanf("%s",buf);
//输入2次名字后,按ctrl+c提前结束程序,发现前两次写入名字失败,因为标准IO存在缓存区
//加上fflush(fp)函数,可实现写入一次成功一次
srtcat(buf,"\n"); //拼接一个\n
fputs(buf,fp);
//写入后手动刷新缓存区
fflush(fp);
}
fclose(fp);
return 0;
}
案例:模仿cp命令,实现内容拷贝,命令行传参./cp a.c b.c
#include <stdio.h>
int main(int argc,const char *argv[])
{
//参数校验
if(argc != 3)
{
printf("传递参数有误\n");
return -1;
}
char buf[128] = {0};
FILE *fp_r = fopen(argv[1],"r");
FILE *fp_w = fopen(argv[2],"w");
if(NULL == fp||NULL == fp1)
{
perror("fopen failed reason");
return -1;
}
//循环读取文件中的所有内容
while(fgets(buf,sizeof(buf),fp_r) != NULL) //文件内容读取
{
fputs(buf,fp_w); //写入另一个文件
}
fclose(fp_r);
fclose(fp_w);
return 0;
}
fread函数
#include <stdio.h>
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
//size_t的类型原型是
typedef int size_t
//调用参考
char buf[100];
fread(buf,50,2,fp); //一个基本单位50,最多读取2块
功能:以指定大小读取文件内容,一次读取一段内容,以块为单位。
参数:void *ptr 读取内容的首地址
size_t size 一块的大小(一块是一个基本单位)以字节为单位
size_t nmemb 一次最多读取的块数
FILE *stream 文件流指针,fopen函数的返回值
返回值:成功,返回实际读取的块数、实际读取的对象数、实际读取的记录数;
失败,返回小于0的数。
案例
#include <stdio.h>
#include <string.h>
int main()
{
FILE *fp = fopen("./fread.c","r");
if(NULL == fp)
{
perror("fopen failed reason");
return -1;
}
char buf[100] = {0};
while(fread(buf,1,sizeof(buf),fp) > 0)
{
printf("%s",buf);
memset(buf,0,sizeof(buf)); //将整个数组都置为0
}
fclose(fp);
return 0;
}
fwrite函数
#include <stdio.h>
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
功能:按照块写入(可以二进制的方式写入)
参数1:即将写入文件数据存放的位置
参数2:一块的大小
参数3:实际想要写入的块数
参数4:fopen函数的返回值
返回值:成功,实际写入的块数;失败,小于0
sprintf和fprintf函数
#include <stdio.h>
int printf(const char *format, ...);
int fprintf(FILE *stream, const char *format, ...);
int sprintf(char *str, const char *format, ...);
memset函数
#include <string.h>
void *memset(void *s, int c, size_t n);
功能:内存设置函数,是一个初始化函数,作用是将某一块内存中的全部设置为指定的值。
返回值:被设置的内存空间的起始地址。
fseek函数
#include <stdio.h>
int fseek(FILE *stream, long offset, int whence);
功能:文件指针是FILE结构中的一个成员变量,移动文件指针的位置
参数1:fopen的返回值
参数2:偏移量(正数---向后移动,负数---向前移动)
参数3:基准值,相对位置
SEEK_SET 文件头
SEEK_END 文件尾
SEEK_CUR 文件当前光标位置
返回值:成功,0;失败,EOF -1
ftell函数
long ftell(FILE *stream);
功能:获取文件指针的位置,文件指针的位置下标从0开始。
参数:fopen的返回值
返回值:成功,返回当前文件光标的位置;失败,EOF。
标准IO三个流(重点)
一个程序运行时,会自动打开三个流。
标准(standard)输入流 | stdin | 默认是键盘 |
标准输出流 | stdout | 默认是终端 |
标准错误流(无缓存) | stderr | 默认是终端 |
time ctime和localtime
#include <time.h>
time_t time(time_t *tloc);
//调用
time(0);
time(NULL); //NULL -- 0
功能:获取从1970-01-01 00:00:00 +0000 (UTC)距今的秒数。
char *ctime(const time_t *timep);
功能:将距今的秒数,转换成英文格式的系统时间
参数:距今的秒数
返回值:英文格式时间字符串的首地址
struct tm *localtime(const time_t *timep);
//
struct tm
{
int tm_sec; /* Seconds (0-60) */
int tm_min; /* Minutes (0-59) */
int tm_hour; /* Hours (0-23) */
int tm_mday; /* Day of the month (1-31) */
int tm_mon; /* Month (0-11) */
int tm_year; /* Year - 1900 */
int tm_wday; /* Day of the week (0-6, Sunday = 0) */
int tm_yday; /* Day in the year (0-365, 1 Jan = 0) */
int tm_isdst; /* Daylight saving time */
};
功能:距今的秒数,转换成年 月 日 小时 分钟 秒 具体时间,格式化为中文格式
使用实例
#include <stdio.h>
#include <time.h>
int main(int argc, const char *argv[])
{
time_t t; // 保存距今的秒数
struct tm *tp = NULL; // 用来保存localtime返回值
time(&t); // 得到距今的秒数
// 因为ctime返回值是英文格式字符串首地址,可以直接用%s输出
printf("%s", ctime(&t));
tp = localtime(&t); // 将距今的秒数转换为具体时间
printf("%d-%02d-%02d %02d:%02d:%02d\n", tp->tm_year + 1900, tp->tm_mon + 1,
tp->tm_mday, tp->tm_hour, tp->tm_min, tp->tm_sec);
return 0;
}