《C和指针》之ANSI C标准输入输出函数


一、I/O流操作一般流程:

(1)为每一个要打开的文件定义一个FILE *类型的指针变量,这个指针变量将指向I/O流使用的FILE结构体。

(2)使用fopen函数打开I/O流。要打开一个I/O流,必须指定要打开的文件(或设备)以及打开后的访问方式(如:只读、只写或读写等)。

(3)按照需要的操作读写文件。

(4)最后,使用fclose函数关闭该I/O流。

在标准流(stdin、stdout和stderr)上进行I/O操作不需要打开和关闭。(stdin、stdout和stderr其实也是执行FILE结构体的指针,它们是由运行时环境提供的)。

I/O函数处理数据的方式分为三种:单字符、文本行和二进制数据。不同的方式使用不同的函数集处理。

二、打开I/O流

1、fopen

FILE *fopen(   *name,   *mode );

参数name和mode都是字符串。

参数name是要打开的文件名,参数mode是打开方式(文本打开方式:“r”、“w”、“a”,二进制打开方式:“rb”、“wb”、“ab”)。

函数返回值由之前定义的FILE *类型的变量保存。,并且失败原因代码记录在errno中。

使用“w”方式打开的文件无论存在还是不存在,都会新建一个name命名的文件(如果存在会先将原来的同名文件删除后再新建)。

使用“a”方式打开的文件如果存在,不删除,打开后位置指针指向移到文件尾,如果文件不存在则新建。

另外,如果在模式字符后加上一个加号“+”,那么无论该模式是只读、只写还是追加,加上加号“+”后都将变为可读写。

通常使用fopen的格式如下:

FILE *= fopen( , ( input ==

2、freopen

FILE *freopen(   *filename,   *mode, FILE *stream );

freopen函数用来打开(或重新打开)一个文件上的特定的流。

参数stream是将要被打开的流。它可能是之前fopen返回的,也可能是标准流(stdin、stdout、stderr)。

freopen函数首先会关闭参数stream代表的流,然后使用给定的文件filename和模式mode重新打开这个流。

三、关闭I/O流

 fclose( FILE *f );

对于输出流(以“w”方式打开的流),fclose会在关闭该流之前冲刷缓冲区(也就是将缓冲区中的内容写到磁盘文件中)。

所有有可能会失败的操作都要进行检查,fclose返回值检查格式如下:

( fclose( input ) != 

四、字符I/O

1、字符输入操作使用getchar函数家族中的函数,这些函数原型如下:

 fgetc( FILE * getc( FILE * getchar(  );

fgetc和getc从指定的流stream中获取要输入的字符,而getchar总是从标准输入中获取要输入的字符。

上面三个函数都是并且将该字符作为函数返回值。

注意:上述三个函数目的都是读取字符,但是函数返回值不是char而是int,其真正原因是为了使得函数可以报告文件结尾EOF。EOF是被定义为int的,这样EOF就在所有可能的字符集范围之外。

2、字符输出操作使用putchar函数家族中的函数,这些函数原型如下:

 fputc(  character, FILE * putc(  character, FILE * putchar(  character );

第一个参数是将要打印的字符。上述函数会将int参数截断为unsigned char,然后再打印。

成功的话,上述函数会返回打印的字符;如果出错,上述函数会返回EOF。

3、撤消字符I/O

 ungetc(  character, FILE *stream );

ungetc函数会将character读回到stream中,以便该字符可以被再次读取,并且ungetc将character作为返回值。需要注意的是,已经读取并存储了该字符的变量值不会受到影响。

通过下面的例子可以更直观的理解:

#include <stdio.h><stdlib.h>
======

*fgets( *buffer, buffer_size, FILE * *gets( * fputs( *buffer, FILE * puts( *buffer );

1、fgets

fgets从指定的流stream中读取字符串,并把读到的字符串拷贝到buffer中。。无论是何种原因导致停止读取,

如果什么都还没有读取就读到了文件末尾,那么buffer不会改变,并且。其返回值经常用来检测是否到达了文件末尾。

2、fputs

传递给fputs的参数buffer必须包含一个字符串,而且这个字符串要以NUL('\0')结尾。这个字符串会被写到参数stream中。对该字符串,fputs是逐个字符进行写入的:如果该字符串中不包含换行符(newline),那么就不会写入换行符;如果该字符串中包含多个换行符,那么这些换行符都会被写入。由此,我们可以看出:

3、gets和puts

gets和puts基本上跟fputs和fgets相同。主要功能上的区别在于:

六、格式化行I/O

1、scanf family

 fscanf( FILE *stream,   * scanf(   * sscanf(   *,   *format, ... );

简单来说,上面三个输入函数主要区别在于输入源不同:fscanf从stream中输入;scanf从标准输入中输入;sscanf则从字符串string中输入。

当到达格式化字符串format结尾或者读到的输入跟格式化字符串不匹配时,会停止输入。

上述函数会将转换的输入个数作为函数返回值。如果一个输入都还没有转换就读到了文件结尾则返回EOF。

 fprintf( FILE *stream,   * printf(   * sprintf(  *buffer,   *format, ... );

简单来讲,printf输出到标准输出;fprintf输出到指定的流stream中;sprintf则将以NUL结尾的字符串输出到指定的buffer中。

七、二进制I/O

fread用来读取二进制数据,fwrite用来写二进制数据。

size_t fread(  *buffer, size_t size, size_t count, FILE * *buffer, size_t size, size_t count, FILE *stream );

buffer是指向保存二进制数据区域的指针。size表示buffer中每个元素的字节数。count表示要读写多少个这样的元素。stream是要读写的流。

fread和fwrite会返回实际读写的元素个数。这个返回值有可能比count要小(由于读到了文件结尾或者写时出错)。

八、冲刷和定位函数

1、fflush

fflush会强制将输出缓冲区中的内容写入到磁盘文件(或设备)中,即使输出缓冲区还没有满,fflush也会强行将其中的内容冲刷出来到它该去的地方去。

 fflush( FILE *stream );

 What does fflush(stdin) do?

This function is used to flush any data in output stream. So this will compile but its behavior is undefined by the ANSI C standard. The fflush() function is only meant to be used on streams open for output, not input.

2、随机访问I/O

要实现随机访问,首先需要定位要访问的位置。以下两个函数就是用来实现文件中位置定位的:

 ftell( FILE * fseek( FILE *stream,  offset,   );

ftell函数会返回I/O流的当前读写位置,它是相对于文件开头的偏移量,也是下次读写的开始位置。对于二进制文件,这个偏移量是从文件开始到当前位置的字节数。然而,在文本文件中,这个偏移量虽然也表示当前位置,但它可能并不是十分精确的表示从文件开头到当前位置的字符数。这是因为行结尾字符在不同系统中可能有所转换。

不过,无论是二进制文件还是文本文件,使用ftell得到的返回值都可以作为fseek中表示相对于文件开头的偏移量。

fseek可以改变下次读写的文件的位置。该位置由参数offset和from共同决定。

from 将会定位到...
SEEK_SET 相对于文件开头offest字节的地方;offset必须为非负。
SEEK_CUR 相对于当前位置offset字节的地方;offset可正可负。
SEEK_END 相对于文件末尾offset字节的地方;offset可正可负。

注意:

(1)试图定位到文件开头之前的位置会出错。

(2)定位到文件末尾之后的位置并进行写入操作会扩展文件。

(3)定位到文件末尾之后的位置并进行读取操作会返回文件结束符(end-of-file)。

(4)对于二进制文件,不支持从SEEK_END开始定位,也应避免这样做。

(5)对于文本文件,如果参数from为SEEK_CUR或者SEEK_END,那么offset必须为0。如果参数from为SEEK_SET,那么offset必须是ftell的返回值。

使用fseek改变文件读写位置后会有3个副作用:

(1)文件尾指示符被清除。

(2)如果ungetc在fseek之前调用,那么撤消的字符将被丢弃(下一次读不到该字符,而是读该字符后面的字符)。

(3)可以从读模式切换到写模式。还可以更新打开的流(类似于fflush的功能)。

 rewind( FILE * fgetpos( FILE *stream, fpos_t * fsetpos( FILE *stream, fpos_t  *position );

rewind设置读写指针重新回到文件开头。它还会清除该流的错误标记。

fgetpos与ftell功能相同,fsetpos与fseek功能相同。

九、设置缓冲区

 setbuf( FILE *stream,  * setvbuf( FILE *stream,  *buf,  mode, size_t size );

上面两个函数只能在特定的流打开以后,但还没有对该流进行任何其他操作的情况下进行调用。

1、setbuf

setbuf函数的功能是:安装一个数组buf来作为流stream的缓冲区。该数组buf大小必须是BUFSIZ(BUFSIZ在stdio.h中定义)。自己为流指定一个缓冲区后,那么会阻止I/O库动态地为该流分配缓冲区。如果调用setbuf时,参数buf为NULL,那么将会关闭该流的所有缓冲区。

2、setvbuf

setvbuf函数更加通用。参数mode用来指示设置何种类型的缓冲区:_IOFBF(全缓冲)、_IONBF(无缓冲)和_IOLBF(行缓冲)。

使用行缓冲的输出流,每当向缓冲区写入换行符(newline)时就冲刷缓冲区buffer。

参数buf和size是用来指定所用的缓冲区的。如果buf为NULL,那么size必须是0。通常情况下,最好使用一个大小为BUFSIZE的数组作为缓冲区。

十、I/O流错误检查函数

 feof( FILE * ferror( FILE * clearerr( FILE *stream ):

如果流当前读写指针位于文件尾,那么feof会返回真。函数fseek、rewind或者fsetpos会清除文件尾标识。

如果流发生了任何读写错误,那么ferror会返回真。

clearerr用来重置流的错误状态标识。

相关内容