目录
4.sprintf/snprintf/fprintf函数的使用
4.1sprintf/snprintf/fprintf函数介绍
1.标准IO缓冲区
1.1缓冲区的种类
- 全缓存:和文件操作相关的缓冲区就是全缓存(fp)
- 行缓存:和终端相关的缓冲区就是行缓存(stdin stdout)
- 不缓存(标准出错stderr)
1.2缓冲区的大小
全缓存大小:4096(4K)
#include <stdio.h>
int main(int argc, const char* argv[])
{
FILE* fp;
if ((fp = fopen("./hello.txt", "w+")) == NULL) {
perror("fopen error\n");
return -1;
}
//必须先使用一下缓冲区,否则缓冲区的大小是0
fputc('u',fp);
//全缓存的大小是4096(4K)
printf("file buffer size = %ld\n",
fp->_IO_buf_end - fp->_IO_buf_base);
fclose(fp);
return 0;
}
行缓存大小:1k
#include <stdio.h>
int main(int argc, const char* argv[])
{
int num;
//标准输入大小验证的时候,必须先使用系统才分配大小,
//否则大小就是0
scanf("%d", &num);
//行缓存(标准输入)大小是1024(1K)
printf("stdin buffer size = %ld\n",
stdin->_IO_buf_end - stdin->_IO_buf_base);
//行缓存(标准输出)大小是1024(1K)
printf("stdout buffer size = %ld\n",
stdout->_IO_buf_end - stdout->_IO_buf_base);
return 0;
}
不缓存大小:0
#include <stdio.h>
int main(int argc, const char* argv[])
{
//使用一下标准出错
fputc('t',stderr);
//打印的大小是1,表示一次接收一个字符,并
//将这个字符直接显示到终端上,所以认为这个
//stderr缓冲区的大小是0
printf("stderr buffer size = %ld\n",
stderr->_IO_buf_end - stderr->_IO_buf_base);
return 0;
}
1.3缓冲区的刷新时机
行缓存的刷新时机
#include <stdio.h>
int main(int argc, const char* argv[])
{
// 1.行缓存遇到换行符的时候会刷新缓冲区
// printf("1111111111111111\n");
// while(1);
// 2.当程序结束的时候会刷新行缓存
// printf("1111111111111111");
// 3.当输入和输出发生切换的时候也会刷新行缓存
// printf("1111111111111111");
// int num;
// scanf("%d",&num);
// while(1);
// 4.当文件指针被关闭的时候也会刷新缓冲区
// printf("1111111111111111");
// fclose(stdout);
// while (1);
// 5.当行缓存满的时候会刷新缓冲区
// for(int i=0;i<=1024;i++){
// fputc('a',stdout);
// }
// while(1);
// 6.使用fflush强制刷新缓冲区
printf("1111111111111111");
fflush(stdout);
while (1);
return 0;
}
全缓存的刷新时机
#include <stdio.h>
int main(int argc, const char* argv[])
{
FILE* fp;
if ((fp = fopen("./hello.txt", "w+")) == NULL) {
perror("fopen error\n");
return -1;
}
// 1.全缓存遇到换行符的时候不会刷新缓冲区
// fputc('y', fp);
// fputc('\n', fp);
// while (1);
// 2.当程序结束的时候会刷新全缓存
// fputc('y', fp);
// 3.当输入和输出发生切换的时候也会刷新全缓存
// fputc('O', fp);
// fgetc(fp);
// while(1);
// 4.当文件指针被关闭的时候也会刷新全缓存
// fputc('W', fp);
// // fclose(fp);
// while (1);
// 5.当全缓存满的时候会刷新缓冲区
// for(int i=0;i<=4096;i++){
// fputc('a',fp);
// }
// while(1);
// 6.使用fflush强制刷新缓冲区
fputc('Q', fp);
fflush(fp);
while (1);
return 0;
}
2.fputs/fgets函数使用
2.1fputs/fgets函数接口介绍
char *fgets(char *s, int size, FILE *stream);
功能:从文件中读取字符串到s中(读取到EOF或者换行符的时候会停止,读取到size的时候会停)
//如果fgets读取到'\n'停止了,会将换行符保存在s中
//在s的buffer中紧接着数据的字符一定会有一个字符标志'\0'
参数:
@s:存放读取到的字符串
@size:读取的大小(最大size-1)
@stream:文件指针
返回值:成功返回s,失败返回NULL
int fputs(const char *s, FILE *stream);
功能:将s中的数据写入到stream中
参数:
@s:被写数据的首地址
@stream:文件指针
返回值:成功返回大于0的数,失败返回EOF(-1)
2.2fgets的实例(stdin)
#include <stdio.h>
#include <string.h>
int main(int argc,const char * argv[])
{
char buf[10] = {0};
//从标准输入中将数据读取到buf中
//最多读取9个字符
fgets(buf,sizeof(buf),stdin);
//将读取到的数据显示到终端上
// printf("buf = %s\n",buf);
//如果buf足够大,在读取到的数据中一定包含一个'\n'
//一般会选择将'\n'处理掉
// 12345'\n''\0'
buf[strlen(buf)-1] = '\0';
printf("buf = %s\n",buf);
return 0;
}
2.3使用fgets统计一个文件的行号
char buf[10] = {0};
#include <head.h>
// /usr/include/
int main(int argc, const char* argv[])
{
FILE* fp;
int line = 0;
char buf[10] = { 0 };
// 1.校验参数
if (argc != 2) {
printf("input error,try again\n");
printf("usage: ./a.out filename\n");
return -1;
}
// 2.打开文件
if ((fp = fopen(argv[1], "r")) == NULL)
PRINT_ERR("fopen error");
#if 0
// 3.读取文件内容,统计行号
while(fgets(buf,sizeof(buf),fp)!=NULL){
if(buf[strlen(buf)-1] == '\n')
line++;
}
//4.输出line+1
printf("line = %d\n",line+1);
#else
// 3.读取文件内容,统计行号
while (fgets(buf, sizeof(buf), fp) != NULL) {
if (strlen(buf) == sizeof(buf) - 1) {
if (buf[strlen(buf) - 1] != '\n') {
continue;
}
}
line++;
}
if (strlen(buf) == sizeof(buf) - 1)
line++;
// 4.输出line+1
printf("line = %d\n", line);
#endif
//5.关闭文件
fclose(fp);
return 0;
}
2.4fputs使用实例(stderr/fp)
#include <head.h>
int main(int argc, const char* argv[])
{
FILE* fp;
int line = 0;
char buf[10] = { 0 };
// 1.校验参数
if (argc != 2) {
fputs("input error,try again\n", stderr);
fputs("usage: ./a.out filename\n",stderr);
return -1;
}
// 2.打开文件
if ((fp = fopen(argv[1], "w")) == NULL)
PRINT_ERR("fopen error");
// 3.向文件中写
fputs("hello\n", fp);
fputs("world\n", fp);
// 4.关闭文件
fclose(fp);
return 0;
}
2.5使用fputs和fgets实现文件的拷贝
#include <stdio.h>
void copy_file(FILE* sfp, FILE* dfp)
{
char buf[128] = { 0 };
while (fgets(buf, sizeof(buf), sfp) != NULL) {
fputs(buf,dfp);
}
}
int main(int argc, const char* argv[])
{
// 1.校验参数
if (argc != 3) {
printf("input error,try again\n");
printf("usage: ./a.out srcfile destfile\n");
return -1;
}
// 2.打开文件
FILE *sfp, *dfp;
if ((sfp = fopen(argv[1], "r")) == NULL) {
perror("fopen srcfile error\n");
return -1;
}
if ((dfp = fopen(argv[2], "w")) == NULL) {
perror("fopen destfile error\n");
return -1;
}
// 3.拷贝
copy_file(sfp, dfp);
// 4.关闭文件
fclose(sfp);
fclose(dfp);
return 0;
}
3.fread/fwrite函数的使用
3.1fread/fwrite函数介绍
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
功能:从文件中读取nmemb项,每一项的大小是size,将读取到的数据存储到ptr中。
参数
@ptr:存储读取到数据的首地址
@size:每一项的大小
@nmemb:项目的个数
@stream:文件指针
返回值:成功返回项目的个数,只要小于项目的个数就是失败
fread读取的数据小于项目个数的时候可能是EOF也可能是失败了,
需要工程师调用feof(fp)或者ferror(fp)来判断
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
功能:将ptr中的数据写入到文件中,写入nmemb项,每一项的大小是size
参数:
@ptr:想要写的数据的首地址
@size:每一项的大小
@nmemb:项目的个数
@stream:文件的指针
返回值:成返回写入项目的个数,小于想写的项目的个数的时候就是失败
3.2fwrite/fread函数使用实例(读写字符串)
fwrite.c
#include <head.h>
int main(int argc,const char * argv[])
{
char buf[128] = {0};
//从终端上输入任意字符到该程序中,
//该程序将输入的字符串写入到hello.txt文件中
//1.从终端向当前的程序读取数据
printf("input > ");
fgets(buf,sizeof(buf),stdin);
buf[strlen(buf)-1] = '\0';
//2.把buf的数据写入到文件中
FILE *fp;
if((fp = fopen("./hello.txt","w"))==NULL)
PRINT_ERR("fopen hello.txt error");
fwrite(buf,1,strlen(buf),fp);
// fwrite(buf,strlen(buf),1,fp);
//3.关闭文件
fclose(fp);
return 0;
}
fread.c
#include <head.h>
int main(int argc, const char* argv[])
{
char buf[128] = {0};
FILE* fp;
if ((fp = fopen("./hello.txt", "r")) == NULL)
PRINT_ERR("fopen hello.txt error");
fread(buf,1,sizeof(buf),fp);
printf("buf = %s\n",buf);
fclose(fp);
return 0;
}
3.3fwrite/fread函数使用实例(读写整数)
fwrite.c
#include <head.h>
int main(int argc, const char* argv[])
{
int num;
printf("input > ");
scanf("%d", &num);
FILE* fp;
if ((fp = fopen("./hello.txt", "w")) == NULL)
PRINT_ERR("fopen hello.txt error");
//将整数写入到hello.txt中,看到的是乱码,原因是文本编辑器
//只失败字符,而1234这个整数没有字符对应,所以看到的就是乱码
//但是按照同种方式读取出来,这个数据依然是没问题的
fwrite(&num, sizeof(num), 1, fp);
fclose(fp);
return 0;
}
fread.c
#include <head.h>
int main(int argc, const char* argv[])
{
int num;
FILE* fp;
if ((fp = fopen("./hello.txt", "r")) == NULL)
PRINT_ERR("fopen hello.txt error");
fread(&num,sizeof(num),1,fp);
printf("num = %d\n",num);
fclose(fp);
return 0;
}
3.4fwrite/fread函数使用实例(读写结构体)
fwrite.c
#include <head.h>
typedef struct {
char name[20];
int age;
} stu_t;
int main(int argc, const char* argv[])
{
stu_t stu = {
"zhangsan",
20,
};
FILE* fp;
if ((fp = fopen("./hello.txt", "w")) == NULL)
PRINT_ERR("fopen hello.txt error");
fwrite(&stu, sizeof(stu), 1, fp);
fclose(fp);
return 0;
}
fread.c
#include <head.h>
typedef struct {
char name[20];
int age;
} stu_t;
int main(int argc, const char* argv[])
{
stu_t stu;
FILE* fp;
if ((fp = fopen("./hello.txt", "r")) == NULL)
PRINT_ERR("fopen hello.txt error");
fread(&stu, sizeof(stu), 1, fp);
printf("name = %s,age = %d\n", stu.name,stu.age);
fclose(fp);
return 0;
}
3.5使用fread/fwrite拷贝文件
#include <stdio.h>
void copy_file(FILE* sfp, FILE* dfp)
{
char buf[128] = { 0 };
int ret;
//当读取到文件结尾的时候feof(sfp)返回真
//如果读取的时候失败了ferror(sfp)也是真
//只有两者同时为假的时候才能循环读写数据
while (!(feof(sfp) || ferror(sfp))) {
ret = fread(buf, 1, sizeof(buf), sfp);
fwrite(buf, 1, ret, dfp);
}
}
int main(int argc, const char* argv[])
{
// 1.校验参数
if (argc != 3) {
printf("input error,try again\n");
printf("usage: ./a.out srcfile destfile\n");
return -1;
}
// 2.打开文件
FILE *sfp, *dfp;
if ((sfp = fopen(argv[1], "r")) == NULL) {
perror("fopen srcfile error\n");
return -1;
}
if ((dfp = fopen(argv[2], "w")) == NULL) {
perror("fopen destfile error\n");
return -1;
}
// 3.拷贝
copy_file(sfp, dfp);
// 4.关闭文件
fclose(sfp);
fclose(dfp);
return 0;
}
4.sprintf/snprintf/fprintf函数的使用
4.1sprintf/snprintf/fprintf函数介绍
int sprintf(char *str, const char *format, ...);
功能:通过格式化控制函数将字符串保存到str对应的内存中了
参数:
@str:存储格式化后字符串的首地址
@format,...:根printf的参数完全相同
返回值:成功返回格式化的字符的个数,失败返回小于0的数
int snprintf(char *str, size_t size, const char *format, ...);
功能:通过格式化控制函数将字符串保存到str对应的内存中了
参数:
@str:存储格式化后字符串的首地址
@size:格式化字符的个数(最多格式化size-1,最后一个是结束符'\0')
@format,...:根printf的参数完全相同
返回值:成功返回格式化的字符的个数,失败返回小于0的数
int fprintf(FILE *stream, const char *format, ...);
功能:通过格式化控制函数将字符串保存到文件
参数:
@stream:文件指针
@format...:根printf的参数完全相同
返回值:成功返回格式化的字符的个数,失败返回小于0的数
4.2sprintf函数实例
#include <head.h>
typedef struct {
char name[20];
int age;
} stu_t;
int main(int argc, const char* argv[])
{
char buf[128] = {0};
stu_t stu = {
"zhangsan",
20,
};
FILE* fp;
if ((fp = fopen("./hello.txt", "w")) == NULL)
PRINT_ERR("fopen hello.txt error");
//将name和age转换成字符串存到buf中
//在使用sprintf的时候一定要保证buf比格式化的字符串成员多
//否则就会越界访问,程序崩溃
sprintf(buf,"%s,%d",stu.name,stu.age);
fwrite(buf,1, strlen(buf), fp);
fclose(fp);
return 0;
}
练习:
向程序输入一个文件名(任意)hello.txt,在程序中创建一个以new_hello.txt命名的空文件
#include <head.h>
int main(int argc, const char* argv[])
{
char filename[128] = {0};
// 1.校验参数
if (argc != 2) {
printf("input error,try again\n");
printf("usage: ./a.out filename\n");
return -1;
}
//2.拼接新的名字
sprintf(filename,"new_%s",argv[1]);
//3.创建文件
FILE *fp;
if((fp = fopen(filename,"w"))==NULL)
PRINT_ERR("fopen file error");
//4.关闭文件
fclose(fp);
return 0;
}
4.3snprintf函数实例
#include <head.h>
typedef struct {
char name[20];
int age;
} stu_t;
int main(int argc, const char* argv[])
{
char buf[8] = {0};
stu_t stu = {
"zhangsan",
20,
};
FILE* fp;
if ((fp = fopen("./hello.txt", "w")) == NULL)
PRINT_ERR("fopen hello.txt error");
//在格式化字符串的时候,字符串的长度超过了sizeof(buf)
//但是最多格式化sizeof(buf)-1字符,剩余没有被格式化的字符
//被忽略掉,虽然编译的时候编译器会发出警告,但是程序执行的
//时候不会崩溃。所以snprintf要比sprintf更为安全。
snprintf(buf,sizeof(buf),"%s,%d",stu.name,stu.age);
fwrite(buf,1, strlen(buf), fp);
fclose(fp);
return 0;
}
练习:请将系统的时间写入到time.txt文件中,每秒写一次。
#include <time.h>
time_t time(time_t *tloc);
功能:获取系统的当前时间(单位是秒,从1970-01-01 00:00:00-2022-08-31 15:55:00)
参数:
@tloc:NULL (通过返回值接收秒钟数)
返回值:成功返回的是秒钟数,失败返回-1置位错误码
struct tm *localtime(const time_t *timep);
功能:将秒钟数转换为struct tm的结构体
参数:
@timep:秒钟变量的地址
返回值:成功返回tm结构体指针,失败返回NULL,置位错误
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) */ //+1
int tm_year; /* Year - 1900 */ //+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; /*夏令时*/
};
#include <head.h>
#include <time.h>
int main(int argc,const char * argv[])
{
time_t ts;
struct tm *tm;
if((ts=time(NULL))==(time_t)-1)
PRINT_ERR("get time second error");
if((tm = localtime(&ts))==NULL)
PRINT_ERR("change time error");
printf("%d-%02d-%02d %02d:%02d:%02d\n",tm->tm_year+1900,
tm->tm_mon+1,tm->tm_mday,tm->tm_hour,tm->tm_min,tm->tm_sec);
return 0;
}
4.4fprintf函数实例
#include <head.h>
int main(int argc,const char * argv[])
{
FILE *fp;
char buf[] = "i am learning io course";
int t=45378;
if((fp = fopen("hello.txt","w"))==NULL)
PRINT_ERR("fopen error");
//将字符串写入到hello.txt文件中
fprintf(fp,"%s,%d\n",buf,t);
//将字符串写入到标准输出中
fprintf(stdout,"%s,%d\n",buf,t);
//将字符串写入到标准出错中
fprintf(stderr,"%s,%d\n",buf,t);
fclose(fp);
return 0;
}
5.作业
将当前的时间写入到tim.txt的文件中,如果ctrl+c退出之后,在再次执行支持"断点续写"
1.2022-03-25 19:10:20
2.2022-03-25 19:10:21
3.2022-03-25 19:10:22
//按下ctrl+c停止,再次执行程序
4.2022-03-25 20:00:00
5.2022-03-25 20:00:01
#include <head.h>
#include <time.h>
int GetFileLine(FILE* fp)
{
int line = 1;
char buf[128] = { 0 };
while (fgets(buf, sizeof(buf), fp) != NULL) {
if (buf[strlen(buf) - 1] == '\n')
line++;
}
return line;
}
int main(int argc, const char* argv[])
{
time_t ts, ots;
struct tm* tm;
int line = 0;
FILE* fp;
// 1.打开文件
if ((fp = fopen("./time.txt", "a+")) == NULL)
PRINT_ERR("fopen error");
// 2.读取文件的行号
line = GetFileLine(fp);
// 3.循环向文件中显示时间
ts = ots = 0;
while (1) { // 2022-9-1 09:13:50
if ((ts = time(NULL)) == (time_t)-1)
PRINT_ERR("get time second error");
if (ts != ots) {
ots = ts;
if ((tm = localtime(&ts)) == NULL)
PRINT_ERR("change time error");
fprintf(fp, "%4d.%d-%02d-%02d %02d:%02d:%02d\n", line++, tm->tm_year + 1900,
tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
fflush(fp);
}
}
fclose(fp);
return 0;
}
#include <head.h>
#include <time.h>
int main(int argc, const char* argv[])
{
time_t ts, ots;
struct tm* tm;
ts = ots = 0;
while (1) { //2022-9-1 09:13:50
if ((ts = time(NULL)) == (time_t)-1)
PRINT_ERR("get time second error");
if (ts != ots) {
ots = ts;
if ((tm = localtime(&ts)) == NULL)
PRINT_ERR("change time error");
//在终端显示时间,在一个位置一直刷新显示
printf("%d-%02d-%02d %02d:%02d:%02d\r", tm->tm_year + 1900,
tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
fflush(stdout);
}
}
return 0;
}