《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( char const *name, char const *mode );
参数name和mode都是字符串。
参数name是要打开的文件名,参数mode是打开方式(文本打开方式:“r”、“w”、“a”,二进制打开方式:“rb”、“wb”、“ab”)。
函数返回值由之前定义的FILE *类型的变量保存。如果成功,则返回一个指向FILE结构体的指针;如果失败,则返回NULL,并且失败原因代码记录在errno中。
使用“r”方式打开的文件应该已经存在,如果文件不存在则出错。
使用“w”方式打开的文件无论存在还是不存在,都会新建一个name命名的文件(如果存在会先将原来的同名文件删除后再新建)。
使用“a”方式打开的文件如果存在,不删除,打开后位置指针指向移到文件尾,如果文件不存在则新建。
另外,如果在模式字符后加上一个加号“+”,那么无论该模式是只读、只写还是追加,加上加号“+”后都将变为可读写。需要注意的是,当你以可读写的方式打开一个文件进行读写操作时:如果你对该文件进行了读操作,那么在你对该文件开始进行写操作之前必须调用文件定位函数(fseek、fsetpos和rewind);在你对该文件进行了写操作之后,那么在你对该文件开始进行读操作之前必须调用fflush或者文件定位函数。
注意:即便是“r”加上了加号“r+”,虽然由只读变为可读写,但是打开时依然要求文件存在,如果不存在则出错。
提醒:无论何时,只要调用fopen函数,务必要检查其返回值。通常使用fopen的格式如下:
FILE *input; input = fopen( "data3", "r" );
if( input == NULL )
{
perror( "data3" );
exit( EXIT_FAILURE );
}
2、freopen
FILE *freopen( char const *filename, char const *mode, FILE *stream );
freopen函数用来打开(或重新打开)一个文件上的特定的流。
参数stream是将要被打开的流。它可能是之前fopen返回的,也可能是标准流(stdin、stdout、stderr)。
freopen函数首先会关闭参数stream代表的流,然后使用给定的文件filename和模式mode重新打开这个流。如果成功,则返回参数stream的值;如果失败,则返回NULL。
三、关闭I/O流
int fclose( FILE *f );
对于输出流(以“w”方式打开的流),fclose会在关闭该流之前冲刷缓冲区(也就是将缓冲区中的内容写到磁盘文件中)。
成功,则返回0;失败,则返回EOF。
所有有可能会失败的操作都要进行检查,fclose返回值检查格式如下:
if( fclose( input ) != )
{
perror( "fclose" );
exit( EXIT_FAILURE );
}
四、字符I/O
1、字符输入操作使用getchar函数家族中的函数,这些函数原型如下:
int fgetc( FILE *stream );
int getc( FILE *stream );
int getchar( void );
fgetc和getc从指定的流stream中获取要输入的字符,而getchar总是从标准输入中获取要输入的字符。
上面三个函数都是从流中读取下一个字符(当然第一次调用这些函数读的是流中的第一个字符,第二次调用就读第二个字符,以此类推)并且将该字符作为函数返回值。如果流中字符被读取完,后面不再有字符,那么返回EOF。
注意:上述三个函数目的都是读取字符,但是函数返回值不是char而是int,其真正原因是为了使得函数可以报告文件结尾EOF。EOF是被定义为int的,这样EOF就在所有可能的字符集范围之外。
2、字符输出操作使用putchar函数家族中的函数,这些函数原型如下:
int fputc( int character, FILE *stream );
int putc( int character, FILE *stream );
int putchar( int character );
第一个参数是将要打印的字符。上述函数会将int参数截断为unsigned char,然后再打印。
成功的话,上述函数会返回打印的字符;如果出错,上述函数会返回EOF。
3、撤消字符I/O
int ungetc( int character, FILE *stream );
ungetc函数会将character读回到stream中,以便该字符可以被再次读取,并且ungetc将character作为返回值。需要注意的是,已经读取并存储了该字符的变量值不会受到影响。
通过下面的例子可以更直观的理解:
#include <stdio.h>
#include <stdlib.h>
void main()
{
char c1;
char c2;
char c3;
char c4;
char c5;
char c6; c1 = getchar();
c2 = getchar();
c3 = getchar();
c4 = ungetc( c3, stdin );
c5 = getchar();
c6 = getchar(); printf( "c1=%c\n", c1 );
printf( "c2=%c\n", c2 );
printf( "c3=%c\n", c3 );
printf( "c4=%c\n", c4 );
printf( "c5=%c\n", c5 );
printf( "c6=%c\n", c6 );
}
五、非格式化行I/O
面向行的I/O可以有两种处理方式:非格式化和格式化。这两种方式的操作对象都是字符串。区别在于,非格式化行I/O只是简单地读写字符串,而格式化行I/O会对数字和其他变量的内部和外部表示形式进行转换。
gets函数家族和puts函数家族针对字符串进行操作,它们的原型如下:
char *fgets( char *buffer, int buffer_size, FILE *stream );
char *gets( char * buffer ); int fputs( char const *buffer, FILE *stream );
int puts( char const *buffer );
1、fgets
fgets从指定的流stream中读取字符串,并把读到的字符串拷贝到buffer中。读到换行符(newline)并将换行符存储到buffer后停止读取。如果已经读取了buffer_size-1个字符,这时也会停止读取。无论是何种原因导致停止读取,停止读取后都会在buffer末尾添加一个字符串结束符NUL('\0')。
如果什么都还没有读取就读到了文件末尾,那么buffer不会改变,并且fgets会返回指针NULL。否则fgets会返回指向buffer的指针。其返回值经常用来检测是否到达了文件末尾。
2、fputs
传递给fputs的参数buffer必须包含一个字符串,而且这个字符串要以NUL('\0')结尾。这个字符串会被写到参数stream中。对该字符串,fputs是逐个字符进行写入的:如果该字符串中不包含换行符(newline),那么就不会写入换行符;如果该字符串中包含多个换行符,那么这些换行符都会被写入。由此,我们可以看出:fgets是一次尽力去读取一整行;而fputs则可以一次写入一行的一部分、一整行或者多行。
如果发生错误,fputs会返回EOF;否则它会返回一个非负值。
3、gets和puts
gets和puts基本上跟fputs和fgets相同。主要功能上的区别在于:
同fgets相比,gets读取输入流中的一行(以换行符结尾),但是它不会存储换行符(newline):也就是说gets会丢弃换行符,然后再在行尾加上一个NUL('\0')。
同fputs相比,puts在写入字符串时,会在字符串后添加一个换行符(newline)。
注意:gets与fgets的另外一个区别是gets没有buffer_size参数。因此gets无法决定buffer的长度。如果读入的行长度超过了buffer所能容纳的大小,那么将会破坏内存中位于buffer之后的毫不相干的变量。即出现了缓冲区越界,这是很危险的。这是gets函数自身存在的一个漏洞,建议慎重使用该函数。
六、格式化行I/O
1、scanf family
int fscanf( FILE *stream, char const *format, ... );
int scanf( char const *format, ... );
int sscanf( char const *string, char const *format, ... );
简单来说,上面三个输入函数主要区别在于输入源不同:fscanf从stream中输入;scanf从标准输入中输入;sscanf则从字符串string中输入。
当到达格式化字符串format结尾或者读到的输入跟格式化字符串不匹配时,会停止输入。
上述函数会将转换的输入个数作为函数返回值。如果一个输入都还没有转换就读到了文件结尾则返回EOF。
fscanf、scanf以及sscanf都会跳过空白字符(包括空格、Tab和换行等)。
2、printf family
int fprintf( FILE *stream, char const *format, ... );
int printf( char const *format, ... );
int sprintf( char *buffer, char const *format, ... );
简单来讲,printf输出到标准输出;fprintf输出到指定的流stream中;sprintf则将以NUL结尾的字符串输出到指定的buffer中。
七、二进制I/O
fread用来读取二进制数据,fwrite用来写二进制数据。
size_t fread( void *buffer, size_t size, size_t count, FILE *stream );
size_t fwrite( void *buffer, size_t size, size_t count, FILE *stream );
buffer是指向保存二进制数据区域的指针。size表示buffer中每个元素的字节数。count表示要读写多少个这样的元素。stream是要读写的流。
fread和fwrite会返回实际读写的元素个数。这个返回值有可能比count要小(由于读到了文件结尾或者写时出错)。
八、冲刷和定位函数
1、fflush
fflush会强制将输出缓冲区中的内容写入到磁盘文件(或设备)中,即使输出缓冲区还没有满,fflush也会强行将其中的内容冲刷出来到它该去的地方去。
int fflush( FILE *stream );
fflush(NULL) flushes all streams opened for output.
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. Both fflush(stdin) and fflush(NULL), in some C libraries, will flush stdout and stderr, but this is completely unportable! Thus, it should not be used.
2、随机访问I/O
要实现随机访问,首先需要定位要访问的位置。以下两个函数就是用来实现文件中位置定位的:
long ftell( FILE *stream );
int fseek( FILE *stream, long offset, int from );
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的功能)。
void rewind( FILE *stream );
int fgetpos( FILE *stream, fpos_t *position );
int fsetpos( FILE *stream, fpos_t const *position );
rewind设置读写指针重新回到文件开头。它还会清除该流的错误标记。
fgetpos与ftell功能相同,fsetpos与fseek功能相同。
九、设置缓冲区
void setbuf( FILE *stream, char *buf );
int setvbuf( FILE *stream, char *buf, int 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流错误检查函数
int feof( FILE *stream );
int ferror( FILE *stream );
void clearerr( FILE *stream ):
如果流当前读写指针位于文件尾,那么feof会返回真。函数fseek、rewind或者fsetpos会清除文件尾标识。
如果流发生了任何读写错误,那么ferror会返回真。
clearerr用来重置流的错误状态标识。
《C和指针》之ANSI C标准输入输出函数的更多相关文章
- C语言——常用标准输入输出函数 scanf(), printf(), gets(), puts(), getchar(), putchar(); 字符串拷贝函数 strcpy(), strncpy(), strchr(), strstr()函数用法特点
1 首先介绍几个常用到的转义符 (1) 换行符“\n”, ASCII值为10: (2) 回车符“\r”, ASCII值为13: (3) 水平制表符“\t”, ASCII值为 9 ...
- <c和指针>学习笔记6输入输出函数
1 错误报告 (1)perror函数 void perror(char const *message) error是标准库的一个外部整型变量(errno.h),保存错误代码之后就会把这个信息传递给用户 ...
- unlocked - 非锁定的标准输入输出函数
SYNOPSIS 总览 #include <stdio.h> int getc_unlocked(FILE *stream); int getchar_unlocked(void); in ...
- 文件输入输出函数fgetc/fputc及fgets/fputs等文件指针位置的变化
文件打开后才可以对文件进行操作.也就是说,文件必须经历打开-操作-关闭的过程.如前所述,C语言对文件的操作都是通过调用标准I/O库函数来实现的.文件操作实际是指对文件的读写.文件的读操作就是从文件中读 ...
- Linux C 程序 输入输出函数(THREE)
标准输入输出函数#include<stdio.h>stdio 是 standard input & output 的缩写 字符数据输入输出函数: putchar() , getch ...
- 【嵌入式开发】C语言 内存分配 地址 指针 数组 参数 实例解析
. Android源码看的鸭梨大啊, 补一下C语言基础 ... . 作者 : 万境绝尘 转载请注明出处 : http://blog.csdn.net/shulianghan/article/detai ...
- C语言 内存分配 地址 指针 数组 参数 实例解析
. Android源码看的鸭梨大啊, 补一下C语言基础 ... . 作者 : 万境绝尘 转载请注明出处 : http://blog.csdn.net/shulianghan/article/detai ...
- C语言-(void*)类型指针
(void*)类型指针:ANSI新增的:即定义了一个指针,但不指定指向任何类型(即指向抽象的数据类型). 1 通过强制类型转换可将其值赋给另一指针变量 2.1用于动态存储函数的返回型指针 void m ...
- C语言采用文本方式和二进制方式打开文件的区别分析
稍微了解C程序设计的人都知道,文本文件和二进制文件在计算机上面都是以0,1存储的,那么两者怎么还存在差别呢?对于编程人员来说,文本文件和二进制文件就是一个声明,指明了你应该以什么方式(文本方式/二进制 ...
随机推荐
- java易混淆概念之类变量、成员变量、局部变量
类变量.成员变量.局部变量 类变量(也叫静态变量)是类中独立于方法之外的变量,用static 修饰.(static表示“全局的”.“静态的”,用来修饰成员变量和成员方法,或静态代码块(静态代码块独 ...
- 利用redis分布式锁的功能来实现定时器的分布式
文章来源于我的 iteye blog http://ak478288.iteye.com/blog/1898190 以前为部门内部开发过一个定时器程序,这个定时器很简单,就是配置quartz,来实现定 ...
- Some_sort_algorithms
复习下数据结构常用的排序算法,更多内容上wiki 快速排序(不稳定 O(n log n)) 快速排序(Quicksort)是对冒泡排序的一种改进.由C. A. R. Hoare在1962年提出.它的基 ...
- [Hive - LanguageManual] Create/Drop/Alter Database Create/Drop/Truncate Table
Hive Data Definition Language Hive Data Definition Language Overview Create/Drop/Alter Database Crea ...
- grep in linux
1.作用linux系统中grep命令是一种强大的文本搜索工具,它能使用正则表达式搜索文本,并把匹配的行打印出来.grep全称是Global Regular Expression Print,表示全局正 ...
- 30 分钟 Java Lambda 入门教程
Lambda简介 Lambda作为函数式编程中的基础部分,在其他编程语言(例如:Scala)中早就广为使用,但在Java领域中发展较慢,直到java8,才开始支持Lambda. 抛开数学定义不看,直接 ...
- JavaSE聊天室
今天学习了一下简单聊天程序(类似QQ那种)的编写过程,从最初的0.1版本到最后的1.3版本,功能不断地增强,下面对一天的学习内容进行梳理. 版本0.1 我们的需求是显示一个窗体,其他什么也不用做,其他 ...
- C#正则表达式判断字符串是否是金钱
public static bool IsMoney(string input) { string pattern = @"^\-{0,1}[0-9]{0,}\.{0,1}[0-9]{1,} ...
- 最新CentOS6.x下redis安装
1:软件环境: 系统版本:CentOS release 6.5 redis版本:redis-cli 3.0.5 安装目录:"/usr/local/redis" 下载软件:" ...
- codeforces 630D Hexagons!
D. Hexagons! time limit per test 0.5 seconds memory limit per test 64 megabytes input standard input ...