嵌入式 IO 2

目录

1.标准IO缓冲区

1.1缓冲区的种类

1.2缓冲区的大小

1.3缓冲区的刷新时机

2.fputs/fgets函数使用

2.1fputs/fgets函数接口介绍

2.2fgets的实例(stdin)

2.3使用fgets统计一个文件的行号

2.4fputs使用实例(stderr/fp)

2.5使用fputs和fgets实现文件的拷贝

3.fread/fwrite函数的使用

3.1fread/fwrite函数介绍

3.2fwrite/fread函数使用实例(读写字符串)

3.3fwrite/fread函数使用实例(读写整数)

3.4fwrite/fread函数使用实例(读写结构体)

3.5使用fread/fwrite拷贝文件

4.sprintf/snprintf/fprintf函数的使用

4.1sprintf/snprintf/fprintf函数介绍

4.2sprintf函数实例

4.3snprintf函数实例

4.4fprintf函数实例

5.作业


1.标准IO缓冲区

1.1缓冲区的种类

  1. 全缓存:和文件操作相关的缓冲区就是全缓存(fp)
  2. 行缓存:和终端相关的缓冲区就是行缓存(stdin stdout)
  3. 不缓存(标准出错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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值