标准I/O

标准I/O是ANSI C建立的一个标准I/O模型,封装了缓冲区,使得在读写文件的时候减少了系统调用的次数,提高了效率。在执行系统调用的时候,Linux必须从用户态切换到内核态,在内核中处理相应的请求,然后再返回用户态。如果频繁地执行系统调用则会增加这种开销。标准I/O为了减少这种开销,采取缓冲机制,为用户空间创建缓冲区,读写时优先操作缓冲区,在必须访问文件时(例如缓冲区满、强制刷新、文件读写结束等情况)再通过系统调用将缓冲区的数据读写实际文件中,从而避免了系统调用的次数

1、标准I/O缓存

1.1、标准的I/O提供了三种类型的缓存。

  1. 全缓存:当缓冲区被填满后才进行实际的I/O操作。 FILE *fp默认大小:4096
  2. 行缓存:当输入或输出中遇到换行符时进行实际的I/O操作。如 stdin(标准输入流)、stdout(标准输出流)默认大小:1024
  1. 无缓存:直接进行实际的I/O操作。如 stderr(标准输出错误流)大小:0

1.2、刷新缓存区的三种方式

  1. 程序结束关闭流
  2. 缓存区填满
  3. 特定命令刷新,\n行缓存默认,fflush

1.2.1 例子:

#include<stdio.h>
#include<unistd.h> // sleep
int main() {

    while (1)
    {
        printf("hello");
        sleep(1);
    }

    return 0;
}

结果:

没有直接输出,输出暂存在缓冲区里了

1.2.2 例子:

#include<stdio.h>
#include<unistd.h> // sleep
int main() {

    while (1)
    {
        printf("hello");
        //printf("hello\n");    // \n 刷新缓冲区
        fflush(stdout);    //刷新缓冲区
        sleep(1);
    }

    return 0;
}

结果:

直接输出了内容。

 

2、流的定义

 标准I/O的核心对象是流。当用标准I/O打开一个文件时,就会创建一个FILE结构体描述该文件。我们把这个FILE结构体称为“流”。标准I/O函数都是基于流进行各种操作的

流就是字节流,分文本流和二进制流,每个ANSI C程序,系统至少提供三个stdin,stdout,stderr。

  •  文本流:文本流是由字符文件组成的序列,每一行包含0个或多个字符并以'\n'结尾。在流处理过程中所有数据以字符形式出   现,'\n'被当做回车符CR和换行符LF两个字符处理,即'\n'ASCII码存储形式是0x0D和0x0A。当输出时,0x0D和0x0A转换   成'\n'
  • 二进制流:二进制流是未经处理的字节组成的序列,在流处理过程中把数据当做二进制序列,若流中有字符则把字符当做ASCII码的二进制数表示。'\n'不进行变换。

(在Linux/Unix系统中,文本流与二进制流没有差异,但是在Windows中稍有差异,所以标准C库定义了两种流。)

默认提供三种流:stdin(标准输入),stdout(标准输出),stderr(标准错误)

 

3、标准i/o函数

3.1 fopen

头 文 件 :#include<stdio.h>

函数原型:FILE *fopen(const char *path, const char *mode)

功    能   :打开一个文件,成功返回流指针,失败返回 NULL

注    意   :fopen创建文件默认权限是 0666 wr-wr-wr linux下umask会影响访问权限,最终权限为(0666 & ~umask)

参    数   :

  • path           路径名(文件名)
  • mode        打开方式
文件打开方式(linux 下文本文件打开与二进制文件打开相同)
     符号                                       意义
    r以读的方式打开文件,如果文件不存在,报错
    w 以写的方式打开文件,如果文件不存在,自动创建,如果文件存在,将文件中的数据清空
    a 以写的方式打开文件,如果文件不存在,自动创建,如果文件存在,将数据写入到文件的末尾
    r+以读写的方式打开文件,如果文件不存在,报错
    w+以读写的方式打开文件,如果文件不存在,自动创建,如果文件存在,将文件中的数据清空
    a+以读写的方式打开文件,如果文件不存在,自动创建,如果文件存在,将数据写入到文件的末尾

3.2 perror

头 文 件 :#include<stdio.h>

函数原型:void perror(const char *s)

功    能   :先输出字符串s 再输出错误号对应的错误信息

3.3 fclose

头 文 件 :#include<stdio.h>

函数原型:int fclose(FILE *stream)

功    能   :关闭一个流,成功返回0,失败返回 EOF

注    意   :流关闭时会刷新缓冲区,程序终止时,所有打开的流都会被关闭

参    数   :stream    流指针

 

3.4 fgetc

头 文 件 :#include<stdio.h>

函数原型:int fgetc(FILE *stream)

功    能   :读取一个字符,成功返回读取的字符,失败返回 EOF

注    意   :getchar()等同于fgetc(stdin)

参    数   :stream    流指针

3.5 fputc

头 文 件 :#include<stdio.h>

函数原型:int fputc(int c, FILE *stream)

功    能   :写入一个字符,成功返回写入的字符,失败返回 EOF

注    意   :putchar(c)等同于fputc(c,stdout)

参    数   :stream    流指针

3.51 例子 用fgetc和fputc复制文件:

#include<stdio.h>

int main(int argc,char *argv[])
{
    FILE *fps,*fpd;
    char ch;
    if (argc < 3) //判断参数个数
    {
        printf("Usage: %s <src_file> <dst_file> \n",argv[0]);
        return -1;
    }
    if((fps = fopen(argv[1],"r")) == NULL) //打开要复制的文件
    {
        perror("fopen src_file ");
        return -1;
    }
    if((fpd = fopen(argv[2],"w")) == NULL) //打开/创建 复制成的文件
    {
        perror("fopen dst_file ");
        return -1;
    }
    while((ch = fgetc(fps)) != EOF) //读到文件末尾返回EOF
    {
        fputc(ch,fpd);
    }
    fclose(fps); //关闭流
    fclose(fpd); //关闭流
    return 0;
}

3.6 fgets

头 文 件 :#include<stdio.h>

函数原型:char *fgets(char *s, int size, FILE *stream)

功    能   :读取一行字符串,成功返回s,到文件末尾出错时返回NULL

注    意   :遇到 '\n' 或已输入size-1个字符时返回,总是包含 '\0'

设缓冲区buf大小为4,输入 abcd\nef  实际存储 a b c /0    大小为6时,实际存储 a b c d \n \0

参    数   :

s      缓冲区地址

size 输入的大小

stream    流指针

3.6.1 例题: 统计一个文本文件包含多少行

#include<stdio.h>
#include<string.h> //strlen()
int main(int argc, char *argv[]) 
{
    FILE *fp;
    char buf[64]; //定义缓冲区
    int line = 0;
    if (argc < 2)
    {
        printf("Usage: %s <dst_file> \n",argv[0]);
        return -1;
    }
    if((fp = fopen("test.txt","r")) == NULL)
    {
        perror("fopen");
        return -1;
    }
    while (fgets(buf, 64, fp) != NULL)
    {
        if(buf[strlen(buf)-1] == '\n') line++;
    }
    close(fp); //注意关闭流
    printf("%d\n",line);
    return 0;
}

 

 

3.6 fputs

头 文 件 :#include<stdio.h>

函数原型:int fgetc(const char *s, FILE *stream)

功    能   :读取一个字符,成功返回输出的字符个数,出错返回 EOF

注    意   :puts()会自动输出\n 而fputs不会

参    数   :

s            缓冲区地址

stream    流指针

 

3.7 fread

头 文 件 :#include<stdio.h>

函数原型:size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream)

功    能   :从文件中读取数据单元,成功返回实际读对象个数,如果小于nmemb,则可能文件结束或读取出错

注    意   :fread不区分文件尾和错误,因此调用者必须用feof和ferror才能判断发生了什么。如果size或nmemb为0,则返回0

参    数   :

ptr           存放读取数据的缓冲区

size         读取的数据单元的大小

nmemb    读取的数据单元的个数

stream    流指针

3.8 fwrite

头 文 件 :#include<stdio.h>

函数原型:size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream)

功    能   :从文件中读取数据单元,成功返回实际写对象个数

注    意   :puts()会自动输出\n 而fputs不会

参    数   :

ptr           存放写入数据的缓冲区

size         写入的数据单元的大小

nmemb    写入的数据单元的个数

stream    流指针

3.81例题:用fread,fwrite复制文件

#include<stdio.h>
#define N 64
int main(int argc, char *argv[])
{
    FILE *fps,*fpd;
    int n;
    char buf[N];
    if (argc < 3)
    {
        printf("Usage: %s <src_file> <dst_file> \n",argv[0]);
        return -1;
    }
    if(NULL == ((fps = fopen(argv[1],"r"))))
    {
        perror("open src_file");
        return -1;
    }
    if(NULL == ((fpd = fopen(argv[2],"w"))))
    {
        perror("open dst_file");
        return -1;
    }
    while (!feof(fps)) //feof 到文件末尾返回1
    {
        n = fread(buf, 1, N,fps); //一个字节大小的单元
        fwrite(buf, 1, n,fpd);    //写入实际读取到的字符的个数
    }
    fclose(fps);
    fclose(fpd);
    return 0;
}

3.9 fprintf、sprintf、printf

头 文 件 :#include<stdio.h>

函数原型:

int fprintf(FILE *fp, const char *fmt, ...)

int sprintf(char *s, const char *fmt, ...)

int printf(const char *fmt, ...)

功    能   :格式化输出,成功返回输出的字符个数,出错返回EOF

参    数   :

fp      流指针

s       缓冲区地址

fmt    输出的格式

3.9.1例子:每隔1秒想文件test.txt中写入当前系统时间

#include<stdio.h>
#include<string.h> //strlen
#include<unistd.h> //sleep
#include<time.h>   //time / localtime

int main()
{
    FILE *fp;
    int line = 0;
    char buf[64];
    time_t t;
    struct tm *tp;
    if(NULL == (fp = fopen("test.txt","r+")))
    {
        perror("fopen");
        return -1;
    }
    while (fgets(buf, 64, fp));
    {
        if(buf[strlen(buf)-1] == '\n') line++;
    }
    while (1)
    {
        time(&t);
        tp = localtime(&t);
        fprintf(fp,"%d: %d-%02d-%02d %02d:%02d:%02d\n",++line,tp->tm_year+1900,tp->tm_mon+1,tp->tm_mday,tp->tm_hour,tp->tm_min,tp->tm_sec);
        fflush(fp); //上面默认为全缓冲 fflush强制刷新一下
        sleep(1);   //睡眠一秒
    }
    
    return 0;
}

 

4、流操作

在打开流的时候,默认偏移位置为0,随着读写的进行,当前读写位置会自动后移,每次后移的大小为实际读写的大小。可以使用fseek()函数和ftell()函数对当前流的偏移量进行定位操作。

4.1 fflush

头 文 件 :#include<stdio.h>

函数原型:int fflush(FILE *fp)

功    能   :刷新流,成功返回0,出错返回EOF

注    意   :Linux下只能刷新输出缓冲区

参    数   :fp    流指针

4.2 ftell

头 文 件 :#include<stdio.h>

函数原型:long ftell(FILE *stream)

功    能   :返回当前读写位置,成功返回当前读写位置,失败返回EOF

参    数   :stream    流指针

4.3 fseek

头 文 件 :#include<stdio.h>

函数原型:long fseek(FILE *stream, long offset, int whence)

功    能   :定位一个流,成功返回0,出错返回EOF

注    意   :whence+offset不能为负数,可以超过文件的长度

参    数   :

stream    流指针

offset      偏移量   相对于第三个参数再次发生的偏移

whence   基准点, SEEK_SET 文件开头   SEEK_CUR 当前位置    SEEK_END 文件末尾

4.31例子:获取文件长度

#include<stdio.h>
int main(int argc, char *argv[])
{
    FILE *fp;
    if(argc < 2)
    {
        printf("Usage: %s <file_name> \n",argv[0]);
        return -1;
    }
    if(NULL == (fp = fopen(argv[1],"r")))
    {
        perror("open");
        return -1;
    }
    fseek(fp,0,SEEK_END);
    printf("file length %ld \n",ftell(fp));
    fclose(fp);
    return 0;
}

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值