15.9 未格式化的行I/O
行I/O可以用两种方式执行---未格式化的和格式化的。这两种形式都用于操纵字符串。区别在于未格式化的I/O(unformatted line I/O)简单读取或写入字符串,而格式化的I/O则执行数组和其他变量在内部和外部表示形式之间的转换。本节将讨论未格式化的行I/O。
gets和puts函数家族用于操作字符串而不是单个字符。这个特征使它们在那些处理一行行文本输入的程序中非常有用。这些函数的原型如下所示:
char *fgets( char *buffer, int buffer_size, FILE *stream );
char *gets( char *buffer );
【C和指针|C和指针 第15章 输入/输出函数 15.9 未格式化的行I/O】int fputs( char const *buffer, FILE *stream );
int puts( char const *buffer );
fgets从指定的stream读取字符并把它们复制到buffer中。当它读取一个换行符并存储到缓冲区之后就不再读取。如果缓冲区存储的字符数达到buffer_size-1个时它也停止读取。在这种情况下,并不会出现数据丢失的情况,因为下一次调用fgets时将从流的下一个字符开始读取。在任何一种情况下,一个NUL字节将被添加到缓冲区所存储数据的末尾,使它称为一个字符串。
如果在读取任何字符前就到达了文件尾,缓冲区就未进行修改,fgets函数返回一个NULL指针;否则,fgets返回它的第1个参数(指向缓冲区的指针)。这个返回值通常只用于检查是否达到了文件尾。
传递给fputs的缓冲区必须包含一个字符串,它的字符被写入到流中。这个字符串预期以NUL字节结尾,所以这个函数没有一个缓冲区长度参数。这个字符串是逐字写入的:如果它不包含一个操作符,就不会写入换行符。如果它包含了好几个换行符,所有的换行符都会被写入。因此,当fgets每次读取一整行时,fputs既可以一次写入一行的一部分,也可以一次写入一整行,甚至可以一次写入好几行。如果写入时出现了错误,fputs返回常量值EOF,否则返回一个非负值。
程序15.3是一个函数,它从一个文件读取输入行并原封不动地把它们写入到另一个文件。常量MAX_LINE_LENGTH决定缓冲区的长度,也就是可以被读取的一行文本的最大长度。在这个函数中,这个值并不重要,因为不管长行是被一次性读取还是分段读取,它所产生的结果文件都是相同的。另一方面,如果函数需要计数被复制的行的数目,太小的缓冲区将产生一个不正确的计数,因为一个长行可能被分成数据段进行读取。我们可以通过增加代码,观察每段是否以换行符结尾来修正这个问题。
缓冲区长度的正确性通常是根据需要执行的处理过程的本质而做出的折衷。但是,即使溢出它的缓冲区,fgets也绝不引起错误。
警告:
注意fgets无法把字符串读入到一个长度小于两字符的缓冲区,因为其中一个字符需要为NUL字节保留。
gets和puts函数几乎和fgets和fputs相同。它们之所以存在是为了允许向后兼容。它们之间的一个主要的功能性区别在于当gets读取一行输入时,它并不在缓冲区存储结尾的换行符。当puts写入一个字符串时,它在字符串写入之后再向输出中添加一个换行符。
警告:
另一个区别仅存于gets,这从函数的原型中就清晰可见:它没有缓冲区长度参数。因此gets无法判断缓冲区的长度。如果一个长输入行读到一个短的缓冲区,多出来的字符将被写入到缓冲区后面的内存位置,这将破坏一个或多个不相关变量的值。这个事实导致gets函数只适用于玩具程序,因为唯一防止输入缓冲区溢出的方法就是声明一个巨大的缓冲区。但不管它有多大,下一个输入行仍有可能比缓冲区更大,尤其是标准输入被重定向到一个文件时。
/*
**把标准输入读取的文本行逐行复制到标准输出。
*/
#include
#define MAX_LINE_LENGTH 1024 /* 可以复制的最长行 */
void copylines( FILE *input, FILE *output ){
char buffer[MAX_LIEN_LENGTH];
while( fgets( buffer, MAX_LINE_LENGTH, input ) != NULL ){
fputs( buffer, output );
}
}
程序15.3 从一个文件向另一个文件复制文本行copyline.c
/*
**把标准输入读取的文本行逐行复制到标准输出。
*/
#include
#include
#define MAX_LINE_LENGTH 1024 /* 可以复制的最长行 */
void copylines( FILE *input, FILE *output );
int main( void ){
FILE *input;
FILE *output;
input = fopen( "data_source.txt", "r" );
printf( "print the content of data_source.txe file:\n" );
int ch;
while( (ch = fgetc( input )) != EOF ){
putchar( ch );
}
printf( "\n" );
if( !input ){
printf( "fail to open data_source.txt file.\n" );
perror( "reason:" );
exit( EXIT_FAILURE );
}
output = fopen( "data_object.txt", "w" );
if( !output ){
printf( "fail to create data_object.txt file.\n" );
printf( "reason:" );
exit( EXIT_FAILURE );
}
/*
** locate the beginning of input stream.
*/
rewind( input );
copylines( input, output );
fclose( input );
output = freopen( "data_object.txt", "r", output );
printf( "after copylines( input, output ), print the content of data_object.txe file:\n" );
while( (ch = fgetc( output )) != EOF ){
putc( ch, stdout );
}
fclose( output );
return EXIT_SUCCESS;
}
void copylines( FILE *input, FILE *output ){
char buffer[MAX_LINE_LENGTH];
while( fgets( buffer, MAX_LINE_LENGTH, input ) != NULL ){
fputs( buffer, output );
}
}
/* 输出:
文章图片
*/