标准IO基本知识及相关API使用方法介绍

标准IO

标准I/O -介绍

标准IO提供了两方面的封装:

1)操作对象 – FILE* 封装了不同OS对文件的实现,

2)封装了缓存支持和优化。


open() //返回fd

fopen() //返回一个  指向FILE的指针
  1. 理解什么是流?
  2. 如何描述一个流?

​ 内存中开辟一个区域,用来存放流的相关信息,这些信息是保存在一个结构体类型的变量之中,该结构体类型是由系统定义的 ,取名为FILE。

3.FILE指针 fopen() 返回 指针FILE结构的指针

流的分类:

文本流和二进制流。(Linux不区分文本流和二进制流)

文本流:存放的字符的ASCLL码

二进制流:存储的方式是二进制

文本流存储的文件一般比二进制流存放的文件要大。

why?可参看下链接文章:

文本流和二进制流_华清远见嵌入式学院的博客-CSDN博客_文本流和二进制流

标准I/O预定义的3个流

一个进程启动的时候,会打开三个流:

标准输入、标准输出、标准出错。

和文件IO中的对应关系。可以查看头文件定义

文件缓冲

首先理解为何要设置缓冲区 – 避免频繁地呼叫系统调用;其次缓冲区大小的设置在不同OS上是有技术的,标准库为我们做了优化选择。

img

0-3GB:为用户空空间

3-4GB: 为内核空间(系统空间) <32位系统>

用户不能操作内核空间,只能使用系统调用才能操作系统(内核)空间

每个进程都有自己的虚拟空间(4G大小~)

缓冲文件系统(高级磁盘IO)

标准IO函数是根据文件流关联的设备类型,会选择采用何种缓冲区的操作方式。分类如下:

1)全缓冲区:这种缓冲区要求填满整个缓冲区后才进行I/O 系统调用操作。**对于磁盘文件通常使用全缓冲区访问。**第一次执行I/O 操作时,ANSI 标准的文件管理函数通过调用malloc 函数获得需使用的缓冲区。linux默认大小为4096。

2)==行缓冲区:==在这种情况下,当在输入和输出中遇到换行符时,标准I/O 库执行I/O系统调用操作当流涉及一个终端时(例如标准输入和标准输出),使用行缓冲区。因为标准I/O 库收集的每行的缓冲区长度是固定的,只要填满了缓冲区,即使还没有遇到换行符也将执行I/O 系统调用操作。默认行缓冲区大小为1024 字节。

3)无缓冲区:标准I/O 库不对字符进行缓存。如果用标准I/O 函数写若干字符到不带

缓冲区的流中,则相当于用write 系统调用函数将这些字符写至相关联的打开文件。标准出错流stderr 通常是不带缓冲区的,这使得出错信息能够尽快地显示出来

非缓冲文件系统(低级磁盘IO)

缓冲区是通过malloc申请的空间

申请的时间: 是发生I/O操作的时候。

标准I/O库函数

IO库函数文件IO函数
fopen(“filepath”,“r”);open(“filepath”,O_RDONLY);
fopen(“filepath”,“r+”);open(“filepath”,O_RDWR);
fopen(“filepath”,“w”);open(“filepath”,O_WRONLY|O_CREAT|O_TRUNC,0666);
fopen(“filepath”,“w+”);open(“filepath”,O_RDWR|O_CREAT|O_TRUNC,0666);
fopen(“filepath”,“a”);open(“filepath”,O_WRONLY|O_CREAT|O_APPEND,0666);
fopen(“filepath”,“a+”);open(“filepath”,O_RDWR|O_CREAT|O_APPEND,0666);
I/O模型文件I/O标准I/O
缓冲方式非缓冲I/O缓冲I/O
操作对象文件描述符*流(FILE )
打开open()fopen()/freopen()/fdopen() ----------- 三个函数的区别: (1)fopen打开路径名由pathname指示的一个文件的路径 (2)freopen常用于一个打开的流重新定向。比如stdout是标准输出,我们可以把它重定向到由path指定的一个文件。重定向的举例见example2。 (3)fdopen取一个现存的文件描述符,并使一个标准的I/O流与该描述符相结合。
关闭close()fclose()
read()fread()/fgetc()/fgets()
write()fwrite()/fputc()/fputs()
定 位lseek()fseek()/ftell()/rewind(): ftell()和fseek(): 这两个函数自V7以来就存在了,但是它们都假定文件的位置可以存放在一个长整型中。 rewind() fsetpos()/fgetpos():这两个函数是新由ANSI C引入的。它们引进了一个新的抽象数据类型fpos_t,它记录文件的位置。 举例:取文件长度 , 注意:ftell不会改变文件位置指针。**
读写流的三种变体

每次一个字符的I/O。使用fgetc()/fputc()一次读或写一个字符,如果流是带缓存的,则标准I/O函数处理所有缓存。注意:fgetc/fputc操作的是一个int,fgetc返回的是int,

变体:getc()/getchar()
        Int c;
        C = fgetc(stdin);
        Fputc(c,stdout);

fgetc和getc基本一致,除了getc()可能是由宏实现的,getchar () == getc(stdin)

每次一行的I/O。使用fgets()和fputs()一次读或写一行。每行都以一个新行符终止。当调用fgets()时,应说明能处理的最大行长。

变体:gets()/puts()
注意1:char *fgets(char *s ,int size,FILE *stream)的用法

fgets每次至多多size-1个字符每次都把都到的字符存储到s字符串数组内,当读到文件末尾(EOF)或者是换行的时候它会结束。换行符(‘/n’),会被存储到字符数组内部,结束时会在字符数组最后一位补一个空位(’/0’)。

如果文件中的该行,不足bufsize个字符,则读完该行就结束。如若该行(包括最后一个换行符)的字符数超过bufsize-1,则fgets只返回一个不完整的行,但是,缓冲区总是以NULL字符结尾,对fgets的下一次调用会继续读该行。

如果n大于一行的字符串长度,那么当读到字符串末尾的换行符时,fgets(…)会返回。并且在s的最后插入字符串结束标志’\0’。 而s缓冲区剩余的位置不会再填充。

注意2:

char *gets(char *s,FILE *stream)

gets不推荐使用,该函数容易造成缓冲区的溢出,造成不良的后果。

二进制直接I/O。fread()和fwrite()函数支持这种类型的I/O。每次I/O操作读或写某种数量的对象,而每个对象具有指定的长度。这两个函数常用于从二进制文件中读或写一个结构。

size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
size_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream);

对fread和fwrite函数重点是:

  1. 注意size_t size, size_t nmemb这两个参数的含义。

    每个元素数据的大小为size,

    读取/写入nmemb个元素数据

  2. 什么情况下需要二进制读写
    (1)两个函数的返回:读或写的对象数
    (2)对于二进制数据我们更愿意一次读或写整个结构。
    (3)为了使用getc()或putc()做到这一点,必须循环读取整个结构,一次读或写一个字节。(效率低)
    (4)fputs()在遇到null字节时就停止,而在结构中可能含有null字节,所以不能使用每次一行函数实现这种要求。如果输入数据中包含有null字节或换行符,则fgets()也不能正确工作。(实现限制)

    读写流结束以及错误判定。EOF/feof()/ferror()/clearer()

    文件I/O和标准I/O区别

    (1)文件I/O是低级I/O,支持POSIX规范,任何支持POSIX标准的操作系统都支持使用文件I/O。(类 UNIX)标准I/O是高级I/O,支持ANSI C相关标准,只要开发环境支持标准C库,标准I/O就可以使用。

    (2)通过文件I/O读取文件时,每次操作都会执行相应的系统调用。好处是直接读取实际文件,
    坏处是频繁调用系统调用,增加系统开销,效率低。标准I/O可以看成在文件I/O的基础之上封装了缓冲机制,先读写缓冲区,必要时读写真实文件,从而减少系统调用的次数。

    (3)文件I/O使用文件描述符,表示一个打开的文件,可以访问不同类型的文件,普通文件,设备文件和管道文件。标准I/O中用FILE(流)来表示一个打开的文件,通常只用来访问普通文件。

Note:多数人认为文件中有一个EOF,用于表示文件的结尾。但这个观点实际上是错误的,在文件所包含的数据中,并没有什么文件结束符。对fgetc或getc而 言,如果不能从文件中读取,则返回一个整数-1,这就是所谓的EOF,返回 EOF无非是出现了两种情况,一是文件已经读完;二是文件读取出错,反正是读不下去了。

检查文件出错函数

#include <stdio.h>

void clearerr(FILE *stream);

int feof(FILE *stream);

int ferror(FILE *stream);

函数描述:

feof:用于检测文件末尾标志,如果该标志被设置(已经到达文件末尾)**返回非0的值,**如果没有被设置返回0

ferror:用于检测出错标志,如果该标志被设置(就是已经出错)返回非0的值,如果没有设置返回0

clearerr:用于清除这两个标志

/***
**读取输出文件内容直到读完整个文件
**用法:
******/

int cTemp;
while(!feof(fp)&&!ferror(stdin))
{
	cTemp = fgetc(fd);
}


练习:

输入字符能将大写字母转换成小写字母,小写字母转换成大写字母。

读写流-行I/O-输入函数

       #include <stdio.h>

       int fgetc(FILE *stream);

       int getc(FILE *stream);

       int getchar(void);

       int ungetc(int c, FILE *stream);




       char *fgets(char *s, int size, FILE *stream);

/****	s:用来存储从制定流的缓冲区中获取的数据首地址
		size:缓冲区的长度
		stream:指定流
	(1)必须指定缓存的长度n,此函数一直读到下一个新行符为止,但是不超过n-1个字符,读入的字符被送入缓存。该缓存以null字符结尾
	(2)如若该行,包括最后一个新行符的字符超过n-1,则返回一个不完整的行,而且缓存总是以null字符结尾。
***/

gets()和fgets() 函数的一个区别是:gets()并不会将一个换行符存入缓存,fgets会将换行符存到缓冲区。

       char *gets(char *s);
//一般不推荐使用,因为不能指定缓冲区的长度,就可能会造成缓存越界。

读写流-行I/O-输出函数

   #include <stdio.h>

   int fputc(int c, FILE *stream);

   int fputs(const char *s, FILE *stream);	//对指定的流里面输出一行

   int putc(int c, FILE *stream);

   int putchar(int c);

   int puts(const char *s);   //标准输出(stdout)输出一行

puts() 和 fputs()区别:

  • puts()函数 会在输出的结尾 加入一个 \n ,而fputs()函数不会再结尾加入 \n
  • puts()函数只能再标准输出(也就是屏幕打印) buf中的数据,而fputs()函数可以自己指定要输出的流。

读写流-行I/O-二进制I/O函数

#include <stdio.h>

size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);

size_t fwrite(const void *ptr, size_t size, size_t nmemb,

在这里插入图片描述

像fputc,fputs,putc,puts 这些函数输出的东西都是 字符或者字符串

但是如果我们需要输出一个结构体该怎么办呢?

size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);

函数参数:

ptr:缓冲区的首地址

sream:流

nmemb:元素的个数

size:每个元素的大小

返回值:

成功 实际读到的元素个数(凑不足一个就不算 返回0)

失败 -1

size_t fwrite(const void *ptr, size_t size, size_t nmemb,

函数参数:

ptr:缓冲区地址

size:元素的大小

nmemb:元素的个数

stream:流

返回值:
成功 实际写入的元素个数

失败 -1

流的格式化输入输出

#include <stdio.h>

int printf(const char *format, ...);
int fprintf(FILE *stream, const char *format, ...);
int sprintf(char *str, const char *format, ...);
#include <stdio.h>

int scanf(const char *format, ...);
int fscanf(FILE *stream, const char *format, ...);
int sscanf(const char *str, const char *format, ...);

标准IO中文件定位函数:

#include <stdio.h>

int fseek(FILE *stream, long offset, int whence);

函数参数:

stream:打开的流

offset:相对于基准点的偏移量

whence:基准点

基准点的三个选项解析:

【1】SEEK_SET:

​ 基准点设置为文件的开始

【2】SEEK_CUR

​ 基准点设置为文件当前位置 指示符

【3】SEEK_END

​ 基准点设置为文件的结尾

返回值

成功 :返回0

失败:返回-1

long ftell(FILE *stream);

返回文件当前位置相对于文件首的偏移量。

void rewind(FILE *stream);

将文件基准点设置为文件的开头。

fseek() 对比文件IO函数 lseek() :

lseek()
成功 返回的是文件的偏移量
失败 返回-1;

fseek()
成功 返回0
失败 返回-1

标准IO函数中要想得到文件的偏移量
就需要使用到 ftell()函数了
(见上)

Homeworks

标准IO实验2

内容:本实验通过一个简单的copy程序,完成文件的复制程序,了解基本的标准IO文件读写的基本步骤。

(三种方式) fgetc/fputc fgets/fputs fread/fwrite

标准IO实验3

编程读写一个文件test.txt,每隔1秒向文件中写入一行数据,类似这样

1、 2000-5-20 15:16:42

该程序应该无限循环,直到按下ctrl+c中断程序,下次在启动程序写文件时可以最佳到原文件的后面

,并且序号能够接续上次的序号。


返回==文件当前位置==相对于==文件首==的偏移量。

```c
void rewind(FILE *stream);

将文件基准点设置为文件的开头。

对比文件IO函数 lseek() :

lseek()成功 返回的是文件的偏移量 失败返回-1;

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值