打开流
以下是三个打开一个标准I/O流的函数
#include <stdio.h>
FILE *fopen(const char *restrict pathname, const char *restrict type);
FILE *freopen(const char *restrict pathname, const char *restrict type, FILE * restrict fp);
FILE *fdopen(int fd, const char *type);
三个函数的返回值:若成功,返回文件指针;若出错,返回NULL。
这三个函数的区别如下:
-
fopen
函数打开路径名为pathname
的一个指定的文件。 -
freopen
函数在一个指定的流上打开一个指定的文件,若该流已经打开,则先关闭该流。若该流已经定向,则使用freopen清除该定向。此函数一般用于将一个指定的文件打开为一个预定义的流:标注输入、标准输出或标准错误。 -
fdopen
函数取一个已有的文件描述符(此文件描述符可以从open
、dup
、dup2
、fcntl
、pipe
、socket
、socketpair
或accept
函数得到),并使一个标准的I/O流与该描述符相结合。此函数常用于创建管道和网络通信通道返回的文件描述符。因为这些特殊类型的文件不能用标准I/O函数fopen
打开,所以必须先调用设备专用函数以获得一个文件描述符,然后用fdopen
使一个标准I/O流与该描述符相结合。fopen
和freopen
是ISO C的所属部分。而ISO C并不涉及文件描述符,所以仅有POSIX.1具有fdopen
。type
参数指定对该I/O流的读、写方式,ISO C规定type
参数可以有15中不同的值。如下表所示:
type | 说明 | open(2)标志 |
---|---|---|
r 或 rb | 为读而打开 | O_RDONLY |
w 或 wb | 把文件截断至0长,或为写而创建 | O_WRONLY | O_CREAT | O_TRUNC |
a 或 ab | 追加:为在文件尾写而打开,或为写而创建 | O_WRONLY | O_CREAT | O_APPEND |
r+ 或 r+b 或 rb+ | 为读和写而打开 | O_RDWR |
w+ 或 w+b 或 wb+ | 把文件截断至0长,或为读和写而打开 | O_RDWR | O_CREAT | O_TRUNC |
a+ 或 a+b 或 ab+ | 为在文件尾读和写而打开或创建 | O_RDWR | O_CREAT | O_APPEND |
在type
参数中将b作为其一部分时,这使得标准I/O系统可以区分文本文件和二进制文件。因为UNIX内核并不对这两种文件进行区分,所以在UNIX系统环境下指定字符b作为type
的一部分实际上并无作用。
对于fdopen
函数,type
参数的意义有些不同。fdopen
函数不能为写而截断其打开的任一文件。除此之外,标准I/O追加写方式也不能用于创建该文件。
当以读和写类型打开一个文件时,具有下列限制。
- 如果中间没有
fflush
、fseek
、fsetpos
或rewind
,则在输出的后面不能直接跟随输入。 - 如果中间没有
fseek
、fsetpos
或rewind
,或者一个输入操作没有到达文件尾端,则在输入操作之后不能直接跟随输出。
调用fclose
函数来关闭一个打开的流。
#include <stdio.h>
int fclose(FILE *fp);
返回值: 若成功,返回 0;若出错,返回EOF。
读和写流
一旦打开了流,则可在三种不同非格式化I/O中进行选择,对其进行读、写操作。
-
每次一个字符的I/O。一次读或写一个字符,如果流是带缓冲的,则标准I/O函数处理所有缓冲。
-
每次一行的I/O。若想要进行一行读或写,则使用
fgets
和fputs
。每行以换行符终止。 -
直接I/O。
fread
和fwrite
函数支持这种类型的I/O。直接I/O在ISO C中被称之为二进制I/O、一次一个对象I/O、面向记录的I/O或面向结构的I/O。
1.输入函数
以下是3个函数可用于一次读一个字符。
#include <stdio.h>
int getc(FILE *fp);
int fgetc(FILE *fp);
int getchar(void);
3个函数返回值:若成功,返回下一个字符;若已到达文件末尾或出错,返回EOF。
不管是出错还是到达文件末尾,这3个函数都返回同样的值,为了区分这两种不同的情况,必须调用ferror
或feof
。
#include <stdio.h>
int ferror(FILE *fp);
int feof(FILE *fp);
上面两个函数返回值:若成功,返回非0;否则,返回0
void clearerr(FILE *fp);
在大多数实现中,为每个流在FILE
对象中维护了两个标志:
- 出错标志
- 文件结束标志。
调用clearerr
可以清除这两个标志。
从流中读取数据以后,可以通过ungetc
将字符再压送回流中。
#include <stdio.h>
int ungetc(int c, FILE *fp);
函数返回值: 若成功,返回c; 若出错,返回EOF
用ungetc压回字符时,并没有将它们写到底层文件中或设备上,只是将它们写回标准I/O库中的流缓冲区中.
2. 输出函数
既然有输入函数就会有对应的输出函数。
#include <stdio.h>
int putc(int c, FILE *fp);
int fputc(int c, FILE *fp);
int putchar(int c);
三个函数返回值: 若成功,返回c; 若出错,返回 EOF。
putchar(c)
等同于putc(c, stdout)
,putc
可被实现为宏,而fputc
不能实现为宏。
每次一行I/O
下面函数提供每次输入一行的功能。
#include <stdio.h>
char *fgets(char *restrict buf, int n, FILE *restrict fp);
char *gets(char *buf);
这两个函数返回值:若成功,返回buf;若已到达文件末尾出错,返回NULL。
注意:
gets
函数是极其不安全,容易造成缓冲区溢出的问题;强烈推荐使用fgets
函数来进行读入一行的操作。
fputs
和puts
提供每次输出一行的功能。
#include <stdio.h>
int fputs(const char *restrict str, FILE *restrict fp);
int puts(const char *str);
两个函数返回值: 若成功,返回非负值;若出错,返回EOF。
puts
函数将一个以null字节终止的字符串写到标准输出,终止符不写出。但是puts
函数随后将一个换行符写到标准输出。fputs
将一个以null字节终止的字符串写到指定的流,尾端的终止符不写出。虽然puts
函数没有像gets
函数那样的安全问题,但是还是应避免使用它,以免需要记住最后是否有添加一个换行符。如果总是使用fgets
和fputs
,需要明确每行终止处的必须处理换行符。
测试示例如下:
#include "../../include/apue.h"
#define MAXSIZE 100
void test1(){
char buf[MAXSIZE] = {0};
while(fgets(buf, MAXSIZE, stdin) != NULL){
printf("buf = ");
fputs(buf, stdout);
}
}
int main()
{
test1();
return 0;
}
结果图如下: