标准IO缓冲区
缓冲区的种类
全缓存:和文件相关的缓冲区就是全缓冲
行缓存:和终端相关的缓冲区就是行缓存
无缓存:标准出错(stderr)是没有缓冲区的。
缓冲区的大小
全缓存:4096(4K)
行缓存:1024(1K)
无缓存:0
#include <stdio.h>
#define PRINT_ERR(msg) \
do { \
perror(msg); \
return -1; \
} while (0)
int main(int argc, const char* argv[])
{
// 缓冲区是在使用的时候才会分配,在不使用的时候缓冲区大小是0
// 在使用标准输入的时候,缓冲区就会分配,分配的大小是1K
int num;
scanf("%d", &num);
printf("stdin size = %ld\n", stdin->_IO_buf_end - stdin->_IO_buf_base);
// 标准输出的大小是1K
printf("stdout size = %ld\n", stdout->_IO_buf_end - stdout->_IO_buf_base);
// 使用标准出错
fputs("sdfsdfsdfsf\n", stderr);
printf("stderr size = %ld\n", stderr->_IO_buf_end - stderr->_IO_buf_base);
//和文件相关的缓冲区的大小是4K
FILE* fp;
if ((fp = fopen("./time.txt", "a+")) == NULL)
PRINT_ERR("fopen error");
// fputc('t',fp);
printf("fp size = %ld\n", fp->_IO_buf_end - fp->_IO_buf_base);
return 0;
}
缓冲区的刷新时机
行缓存的刷新时机
#include <stdio.h>
int main(int argc, const char* argv[])
{
// 1.行缓存遇到换行符的时候会刷新缓冲区
// printf("hello DC22091 everyone!!!\n");
// while(1);
// 2.程序结束的时候也会刷新行缓存
// printf("hello DC22091 everyone!!!");
// 3.当输入和输出发生切换的时候也会刷新缓冲区
// printf("hello DC22091 everyone!!!");
// int num;
// scanf("%d",&num);
// while(1);
// 4.当关闭对应的文件指针的时候也会刷新缓冲区
// printf("hello DC22091 everyone!!!\n");
// fclose(stdout);
// while(1);
// 5.当缓冲区满的时候也会刷新缓冲区,1024字符被刷新出来,多余1024个的字符
// 重新被放到缓冲区中
// for(int i=0;i<1025;i++){
// fputc('a',stdout);
// }
// while(1);
//6.使用fflush强制刷新缓冲区
printf("hello DC22091 everyone!!!\n");
fflush(stdout);
while(1);
return 0;
}
全缓存的刷新时机
#include <head.h>
int main(int argc, const char* argv[])
{
FILE *fp;
if((fp = fopen("./hello.txt","w+"))==NULL)
PRINT_ERR("fopen error");
// 1.全缓存遇到换行的时候不会刷新缓冲区
// fputs("hello DC22091 everyone!!!\n",fp);
// while(1);
// 2.程序结束的时候也会刷新全缓存
// fputs("hello DC22091 everyone!!!",fp);
// 3.当输入和输出发生切换的时候也会刷新缓冲区
// fputs("hello DC22091 everyone!!!",fp);
// fgetc(fp);
// while(1);
// 4.当关闭对应的文件指针的时候也会刷新缓冲区
// fputs("hello DC22091 everyone!!!\n",fp);
// fclose(fp);
// while(1);
// 5.当缓冲区满的时候也会刷新缓冲区,4096字符被刷新出来,多余4096个的字符
// 重新被放到缓冲区中
// for(int i=0;i<4097;i++){
// fputc('a',fp);
// }
// while(1);
//6.使用fflush强制刷新缓冲区
fputs("hello DC22091 everyone!!!\n",fp);
fflush(fp);
while(1);
return 0;
}
标准IO函数接口
fprintf函数
fprintf函数的API
int fprintf(FILE *stream, const char *format, ...);
功能:直接将格式化后的字符串写入到文件中
参数:
@stream:文件指针
@format,...控制格式
返回值:成功返回大于0的数,失败返回小于0的数
fprintf函数的实例
#include <stdio.h>
#include <time.h>
#define PRINT_ERR(msg) \
do { \
perror(msg); \
return -1; \
} while (0)
int get_file_line(FILE* fp)
{
char ch = 0;
int line = 0;
while ((ch = fgetc(fp)) != EOF) {
if (ch == '\n') {
line++;
}
}
return line;
}
int main(int argc, const char* argv[])
{
FILE* fp;
time_t ts, ots;
struct tm* tm;
int line = 0;
char buf[128] = { 0 };
#if 0
//当程序需要命令行传递参数的时候,参数不对可以使用fprintf直接
//向标准出错中写
if(argc !=2){
fprintf(stderr,"input err,try again\n"); //向标准错误中写字符串
fprintf(stderr,"usage:./a.out filename\n");
return -1;
}
#endif
if ((fp = fopen("./time.txt", "a+")) == NULL)
PRINT_ERR("fopen error");
line = get_file_line(fp);
ts = ots = 0;
while (1) {
if ((ts = time(NULL)) == -1)
PRINT_ERR("get time 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); // 强制刷新缓冲区
}
}
return 0;
}
fread/fwrite函数的使用
fread/fwrite函数的API
s
ize_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
功能:从文件中读取nmemb项,每一项的大小是size,将读取到的内容存到ptr中
参数:
@ptr:指向存储数据的地址
@size:每一项的大小
@nmemb:项目的个数
@stream:文件指针
返回值:成功返回读取到的项目的个数,小于想要读取的项目的个数的时候
就是错误或者到了文件的结尾。
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:文件指针
返回值:成功返回写入项目的个数,小于项目的个数的时候就是失败
fwrite函数的实例
#include <head.h>
typedef struct {
char name[20];
int age;
char sex;
} stu_t;
int main(int argc, const char* argv[])
{
FILE* fp;
if ((fp = fopen("./hello.txt", "w")) == NULL)
PRINT_ERR("fopen error");
// 1.向文件中写入整数
int num = 12345;
fwrite(&num, sizeof(num), 1, fp);
// 2.写入字符串
char buf[] = "abcdefghi***";
fwrite(buf, strlen(buf), 1, fp);
// 3.向文件中写入结构体
stu_t st[] = {
[0] = {
.name = "zhangsan",
.age = 20,
.sex = 'm',
},
[1] = {
.name = "lisi",
.age = 18,
.sex = 'w',
}
};
fwrite(st, sizeof(stu_t), 2, fp);
fclose(fp);
return 0;
}
fread函数的实例
#include <head.h>
typedef struct {
char name[20];
int age;
char sex;
} stu_t;
int main(int argc,const char * argv[])
{
FILE *fp;
if((fp = fopen("./hello.txt","r"))==NULL)
PRINT_ERR("fopen error");
//1.向文件中写入整数
int num;
fread(&num,sizeof(num),1,fp);
printf("num = %d\n",num);
//2.读取字符串
int ret;
char buf[128] = {0};
ret = fread(buf,1,12,fp);
printf("buf = %s,ret = %d,eof = %d\n",buf,ret,feof(fp));
//3.读取结构体
stu_t st[2];
fread(st,sizeof(stu_t),2,fp);
printf("name = %s,age = %d,sex = %c\n",st[0].name,st[0].age,st[0].sex);
printf("name = %s,age = %d,sex = %c\n",st[1].name,st[1].age,st[1].sex);
fclose(fp);
return 0;
}
练习
1.使用fread/fwrite拷贝文件
./a.out srcfile destfile
#include <head.h>
int main(int argc, const char* argv[])
{
FILE *fp1, *fp2;
char buf[128] = {0};
int ret;
// 1.校验参数
if (argc != 3) {
fprintf(stderr, "input error,try again\n");
fprintf(stderr, "usage:./a.out srcfile destfile\n");
return -1;
}
// 2.打开源和目标文件
if ((fp1 = fopen(argv[1], "r")) == NULL)
PRINT_ERR("fopen src error");
if ((fp2 = fopen(argv[2], "w")) == NULL)
PRINT_ERR("fopen dest error");
//3.循环拷贝
#if 1
while(!(feof(fp1) || ferror(fp1))){
ret = fread(buf,1,sizeof(buf),fp1);
fwrite(buf,1,ret,fp2);
}
#else
while (!(feof(fp1) || ferror(fp1))) {
fread(buf, sizeof(buf) - 1, 1, fp1);
fwrite(buf, 1, strlen(buf), fp2);
memset(buf,0,sizeof(buf));
}
#endif
//4.关闭文件
fclose(fp1);
fclose(fp2);
return 0;
}
光标操作相关函数
fseek/rewind/ftell函数的API
int fseek(FILE *stream, long offset, int whence);
功能:修改光标的位置
参数:
@stream:文件指针
@offset:修改的偏移量
>0:向后移动光标
=0:将光标修改当开头,结尾,当前位置
<0:向前移动光标
@whence:从那个位置开始修改光标
SEEK_SET :文件开头
SEEK_CUR :光标当前位置
SEEK_END :从文件结尾开始修改
返回值:成功返回0,失败返回-1置位错误码
eg:
fseek(fp,50,SEEK_SET); //将光标定位在第50个字节的位置
fseek(fp,0,SEEK_END); //将光标定位到文件的结尾
fseek(fp,-10,SEEK_CUR); //将光标从当前位置向前移动10个字节的位置
void rewind(FILE *stream);
功能:将光标定位到文件开头
参数:
@stream:文件指针
返回值:无
rewind(fp) = fseek(fp,0,SEEK_SET);
long ftell(FILE *stream);
功能:返回光标到文件开头的字节数
参数:
@stream:文件指针
返回值:成功返回字节数,失败返回-1,置位错误码
fseek/rewind/ftell函数实例
实例1:使用fseek修改光标的位置
hello.txt
1234567890
abcdefg
fseek.c
#include <head.h>
int main(int argc, const char* argv[])
{
FILE* fp;
if ((fp = fopen("./hello.txt", "r+")) == NULL)
PRINT_ERR("fopen error");
fseek(fp,5,SEEK_SET);
printf("get = %c\n",fgetc(fp));
fputc('T',fp);
fclose(fp);
return 0;
}
实例2:使用fseek修改追加的光标
#include <head.h>
int main(int argc, const char* argv[])
{
FILE* fp;
if ((fp = fopen("./hello.txt", "a+")) == NULL)
PRINT_ERR("fopen error");
fputc('T', fp); // 写在了结尾的位置
fseek(fp, 5, SEEK_SET); // 从开头修改光标
printf("get = %c\n", fgetc(fp)); // 读取的是6字符
fseek(fp, 8, SEEK_SET);
fputc('W', fp); //写时在结尾
//总结:对于a+的方式打开文件,能通过fseek修改读光标的位置
//但是不能修改写光标的位置,写永远是在结尾的。
fclose(fp);
return 0;
}
实例3:使用fseek和ftell统计文件大小
#include <head.h>
int main(int argc, const char* argv[])
{
FILE* fp;
if ((fp = fopen("./hello.txt", "r")) == NULL)
PRINT_ERR("fopen error");
fseek(fp,0,SEEK_END);
printf("len = %ld\n",ftell(fp));
fclose(fp);
return 0;
}
标准IO的练习
bmp图片格式
bmp文件头(bmp file header):提供文件的格式、大小等信息 (14字节)
位图信息头(bitmap information):提供图像数据的尺寸、位平面数、压缩方式、颜色索引等信息(40字节)
位图数据(bitmap data):就是图像数据
RGB888 (一个像素点占3个字节)
RGB565 (一个像素点占2个字节)
注:bmp格式的图片,它的行上下倒置。
代码实例
#include <head.h>
typedef struct {
unsigned char b;
unsigned char g;
unsigned char r;
} RGB_t;
int main(int argc, const char* argv[])
{
FILE* fp;
unsigned int size, width, high;
unsigned short piex;
// 1.校验参数
if (argc != 2) {
fprintf(stderr, "input error,try again\n");
fprintf(stderr, "usage:./a.out xxxx.bmp\n");
return -1;
}
// 2.打开图片
if ((fp = fopen(argv[1], "r+")) == NULL)
PRINT_ERR("fopen error");
// 3.读取数据
fseek(fp, 2, SEEK_SET);
fread(&size, 4, 1, fp);
printf("size = %d\n", size);
fseek(fp, 18, SEEK_SET);
fread(&width, 4, 1, fp);
printf("width = %d\n", width);
fread(&high, 4, 1, fp);
printf("high = %d\n", high);
fseek(fp, 2, SEEK_CUR);
fread(&piex, 2, 1, fp);
printf("piex = %d\n", piex);
RGB_t rgb = {
0, 0, 255
};
fseek(fp, 54, SEEK_SET);
for (int i = 0; i < 50; i++) {
for (int j = 0; j < width; j++) {
fwrite(&rgb, sizeof(rgb), 1, fp);
}
}
// 4.关闭文件
fclose(fp);
return 0;
}
练习:给叮当猫的鼻子换成绿色
#include <head.h>
typedef struct {
unsigned char b;
unsigned char g;
unsigned char r;
} RGB_t;
int main(int argc, const char* argv[])
{
FILE* fp;
unsigned int size, width, high;
unsigned short piex;
// 1.校验参数
if (argc != 2) {
fprintf(stderr, "input error,try again\n");
fprintf(stderr, "usage:./a.out xxxx.bmp\n");
return -1;
}
// 2.打开图片
if ((fp = fopen(argv[1], "r+")) == NULL)
PRINT_ERR("fopen error");
// 3.读取数据
fseek(fp, 2, SEEK_SET);
fread(&size, 4, 1, fp);
printf("size = %d\n", size);
fseek(fp, 18, SEEK_SET);
fread(&width, 4, 1, fp);
printf("width = %d\n", width);
fread(&high, 4, 1, fp);
printf("high = %d\n", high);
fseek(fp, 2, SEEK_CUR);
fread(&piex, 2, 1, fp);
printf("piex = %d\n", piex);
RGB_t rgb = {
0, 255, 0
};
fseek(fp, 54+362*1200*3+566*3, SEEK_SET);
for (int i = 0; i < 70; i++) {
for (int j = 0; j < 73; j++) {
fwrite(&rgb, sizeof(rgb), 1, fp);
}
fseek(fp,(width-73)*3,SEEK_CUR);
}
// 4.关闭文件
fclose(fp);
return 0;
}