基于流的I/O操作

时间:2023-02-17 16:32:16

.I/O操作过程归纳:对流进行操作的第一步是通过调用fopen()函数将其打开,并返回一个FILE结构指针。当流成功打开以后,就可以调用相应的库函数对其进行I/O操作了。当完成操作后,需要执行清空缓冲区、保存数据等操作,然后将流关闭,这些工作可以通过调用fclose()函数来完成。注意,如果不关闭流,可能造成数据的丢失。

 

.缓存:基于流的操作最终会调用read或者write函数进行I/O操作。为了提高程序的运行效率,尽可能减少使用readwrite调用的数量,流对象通常会提供缓冲区(也叫做缓存,二者概念完全等同),以减少调用系统I/O库函数的次数。标准I/O提供了3种类型的缓存:

1.全缓存:直到缓冲区被填满,才调用系统I/O函数。如磁盘文件通常是全缓冲的。宏: _IO_FULL_BUF

2.行缓存:直到遇到换行符“\n”,才调用系统I/O库函数。如:stdinstdout。宏: _IO_LINE_BUF

3.无缓存:没有缓冲区,数据会立即读入或者输出到外存文件和设备上。如:stderr。宏: _IO_UNBUFFERED

(使用这几个宏的时候应该将文件流对象中的缓冲区标志与该宏进行逻辑操作,通过判断结 果是否为0即可知道该文件流的缓冲区属于何种类型了。)

 

.对缓存的操作:

1.设置缓存的属性: #include <stdio.h>

void setbuf(FILE* fp, char* buf);

void setbuffer(FILE* fp, char* buf, size_t size);

void setlinebuf(FILE* fp);

int setvbuf(FILE* fp, char* buf, int mode, size_t size);

返回:前3个函数没有返回值。setvbuf函数的返回:若成功则为0,出错则 为非0.

2.缓存的冲洗:所谓缓存的冲洗,是指将I/O操作写入缓存中的内容清空。这种清空可以是将流的内容完全丢掉,也可以是将其保存到文件中,相应的库函数如下:

#include <stdio.h>

int fflush(FILE* fp);

#include <stdio_ext.h>

void fpurge(FILE* fp);

fflush函数用于将缓冲区中尚未写入文件的数据强制性地保存到文件中。调 用成功时,返回值为0;调用失败则返回EOF

purge函数用于将缓冲区的数据完全清楚。由于使用较少,这个函数的定义 <stdio_ext.h>

 

.流的打开与关闭:

1.流的打开: #include <stdio.h>

FILE* fopen(const char* pathname, const char* type);

FILE* freopen(const char* pathname, const char* type, FILE* fp);

FILE* fdopen(int fd, const char* type);

3个函数的返回:若成功则返回文件指针,若出错则为NULL(空指针)。

freopen一般用于将以个指定的文件打开为一个预定义的流。

fdopen常用于由创建管道和网络通信通道函数获得的描述符。因为 这些特殊类型的文件不能使用标准I/Ofopen函数打开,首先必 须先调用设备专用函数以获得一个文件描述符,然后用fdopen使一 个标准I/O流与该文件描述符相结合。由于文件已经被打开,所以 fdopen函数不会创建文件,也不会将文件截短为0,这一点要特别 注意。这两步操作在打开该文件描述符的时候已经完成。

 

2.流的关闭: #include <stdio.h>

int fclose(FILE* fp);

返回:成功则返回0,失败则返回EOF

3.流关闭前的工作:大多数流的操作都是基于缓冲机制的(stderr无缓冲的),那么就 会出现一个问题:当关闭一个流时,那些缓存在缓冲区的、还未来的及写入文件或设备 的数据怎么办呢?不必担心,fclose函数替我们做好了这些工作。

fclose函数关闭文件时,会将保存在内存中尚未来得及写回磁盘的文件内容写到磁盘上。 实际上,fclose函数在关闭流之前,还进行了最后一次写文件的操作。了解这一点很有 必要,因为如果没有调用fclose函数,就必须等待内存中缓冲区被填满,由系统将其内 容写回到磁盘上区,这也是在流操作完成之后必须关闭他的原因。

 

.流的读写:

1.基于字符的I/O

1.1字符的输入:#include <stdio.h>

  int getc(FILE* fp);

  int fgetc(FILE* fp);

  int getchar(void);

  3个函数返回:若成功则返回读入字符的值,若已处文件尾端或出   错则为EOF,前两个函数中,参数fp表示要读入字符的文件,它   们的区别是getc可被实现为宏,而fgetc不能实现为宏。这意味着:   getc的参数不应当是具有副作用的表达式。

  #include <stdio.h>

  int ferror(FILE* fp);

  int feof(FILE* fp);

  void clearerr(FILE* fp);

  两个函数返回:若条件为真则为非0(),否则为0()

 

  注意:大多数UNIX系统的FILE对象中,为每个流保持了两个标   志:出错标志和文件结束标志。调用clearerr可以清楚这两个标志。

  

  #include <stdio.h>

  int ungetc(int c, FILE* fp);

  返回:若成功则返回要送回流中的字符的值,若出错则为EOF

 

1.2字符的输出:#include <stdio.h>

  int putc(int c, FILE* fp);

  int fputc(int c, FILE* fp);

  int putchar(int c);

     3个函数返回:若成功则为c,若出错则为EOF

  putc()fputc函数的第1个参数表示需要输出的字符,第2个参数   表示输出的文件。如果成功输出一个字符,则返回输出的字符,   如果出错则返回EOF

 

2.基于行的I/O

2.1行的输入: #include <stdio.h>

char* fgets(char* buf, int n, FILE* fp);

char* gets(char* buf);

两个函数返回:若成功则返回缓冲区的首地址,若已处文件尾或出 错则为NULL

gets函数的缺陷:gets函数和fgets函数最大的不同是gets函数的缓 冲区虽然由用户提供,但是用户无法指定其一次最多读入多少字节 的内容,这一点导致gets变成了一个非常危险的函数。

2.2行的输出: #include <stdio.h>

int fputs(const char* buf, FILE* restrict fp);

int puts(const char* str);

fputs函数的第1个参数表示存放输出内容的缓冲区,第2个参数表 示要输出的文件。如果成功输出,则返回输出的字节数,失败则返 -1.

puts函数用于向标准输出输出一行字符串,其参数和fputs函数的第 1个参数相同,如果成功输出,则返回输出字节数,失败则返回-1.

fputs函数和puts函数都不输出字符串的结束符'\0'puts函数不想 gets函数那样有致命的漏洞,但是其安全程度也不是很高,所以 我依然推荐使用fputs函数。对于行I/O来说,fgets函数和fputs 数的搭配是安全又可靠的。

3.直接I/O

#include <stdio.h>

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

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

两个函数返回:读或写的对象数。(用于执行二进制I/O)

fread函数用于执行直接输出操作。参数ptr是指向读取数据的缓冲 区的指针,size是读取对象的大小,nmemb表示欲读取的对象的个 数,fp是指向要读取的流的FILE结构的指针。

fwrite函数用于执行直接输入操作。参数ptr是指向存放将要输入数 据的缓冲区的指针,size是写入对象的大小,nmemb表示欲写入的 对象的个数,fp是指向要写入的流的FILE结构指针。

(使用feof函数和ferror函数判断判断是出错还是是否到文件末尾)

 

4.格式化I/O

4.1格式化输出:#include <stdio.h>

  int printf(const char* format, ...);

  int fprintf(FILE* fp, const char* format, ...);

  两个函数返回:若成功则为实际输出的字符数, 若输出出错则为   负值。

         int sprintf(char* str, const char* format, ...);

  int snprintf(char* str, size_t size, const char* format, ...);

  两个函数返回:若成功则为实际存入缓冲区的字符数,若出错则   为负值。

  sprintf函数用于向指定的流中输出一个字符串,sprintf字符串的   尾端自动添加一个NULL字节,但该字节不包括在返回值中。

  snprintf函数的作用个sprinif函数,但snprintf函数可以处理缓冲   区,参数size用于设定缓冲区大小。

4.2格式化输入:#include <stdio.h>

  int scanf(const char* format, ...); //用于stdin

  int fscanf(FILE* fp, const char* format, ...); //用于指定的流

  int sscanf(char* str, const char* format, ...); //从一个字符串中

  3个函数返回:成功则为指定的输入项数,出错或已经到了文件末   尾则为EOF