【IO进程线程】

本文详细介绍了标准IO中的缓冲区类型,包括全缓存、行缓存和无缓存,以及它们的大小和刷新时机。同时,文章通过示例展示了如何使用fprintf、fread和fwrite等标准IO函数进行文件读写,并探讨了fseek、rewind和ftell在光标操作中的应用。最后,文章提供了关于BMP图片格式的解析和编辑示例。
摘要由CSDN通过智能技术生成

标准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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值