2 C 标准I/O
2.1 文件指针
- 标准I/O并不直接操作文件描述符,而是通过文件指针(file pointer)。文件指针映射到一个文件描述符。文件指针类型为
FILE
,定义在头文件<stdio.h>
- 在标准I/O中,一个打开的文件叫做“流”(stream)。
- 标准输入 输出 错误的流通过预定义文件指针
stdin
,stdout
,stderr
加以引用
2.2 打开文件
- 文件通过
fopen()
打开以提供读写操作:打开文件并为它关联一个新的流
#include <stdio.h>
FILE* fopen(const char * path, const char * mode);
//成功返回文件指针,否则返回NULL
- mode
mode | 说明 |
---|---|
r | 打开文件用来读,流定位在文件开始处 |
r+ | 打开文件用来读写,流定位在文件开始处 |
w | 打开文件用来写,如果文件存在,文件会被清空。如果不存在,会被创建。流定位在文件开始。 |
a | d打开文件用来追加模式的写入。如果文件不存在它会被创建。流被设置在文件的末尾,所有写入都会接在文件后。 |
a+ | 打开文件用来追加模式的读写。如果文件不存在它会被创建。流被设置在文件的末尾,所有写入都会接在文件后。 |
b | 表示二进制形式,这个值在linux下被忽略,因为文本文件和二进制文件都一视同仁 |
- 通过文件描述符打开文件
- 函数
fdopen()
将一个已经打开的文件描述符转换成流
#include <stdio.h>
FILE* fdopen(int fd, const char * mode);
- 可能模式与上面一样,但必须和原来打开文件描述符的模式匹配。可以指定w,w+,但是他们不会截断文件。流的位置设置在文件描述符指向的文件位置。可以但不要在打开流的情况下直接操作描述符。关闭流也会关闭描述符。
2.3 关闭流
- fclose()函数关闭一个给定的流
#include <stdio.h>
int fclose(FILE * stream);
//成功返回0,失败返回EOF(-1),设置errno
- fcloseall()关闭所有和当前程序关联的流,包括标准输入,输出,错误
#include <stdio.h>
int fcloseall(void);
//该函数始终返回0;该函数是linux特有的
3.3 从流中读取数据
- 单字节读取
#include <stdio.h>
int fgetc(FILE * stream);
//读取一个字符,并把该无符号字符强转成int返回。文件结尾,错误都返回EOF(-1)
- 把字符放回流中
- 允许你偷窥流,如果不需要,可以把它放回
#include <stdio.h>
int ungetc(int c,FILE * stream);
//把c强转为无符号字符,放回流中,成功返回c,否则返回EOF
//如果使用了一次定位函数,所有推回的字符被丢弃
- 按行读取
#include <stdio.h>
char * fgets(char *str, int size, FILE * stream);
//从流中读取最多size-1个字节数据,遇到文件尾或换行时读入结束,如果读到‘\n’
//也放入str,最后把空字符'\0'放到str结尾。成功返回str,失败返回NULL
- 读取二进制文件
#include <stdio.h>
size_t fread(void *buf, size_t size, size_t nr, FILE* stream);
//从流中读取nr个数据,每个数据大小为size,放入buf中
//实际读入的个数被返回(注意不是字节数)
//返回小于nr的值时读取失败或文件结束,用ferror()和feof()判断
3.4 向流中写数据
#include <stdio.h>
//写入单个字符
int fput(int c, FILE * stream);
//成功返回c,否则返回EOF
//写入字符串
int fputs(const char *str, FILE *stream);
//将str中'\0'前部分写入stream指向的流中,成功返回非负整数,失败EOF
//写入二进制数据
size_t fwrite(void *buf, size_t size, size_t nr, FILE *stream);
//把buf指向的nr个元素(每个元素大小为size)写入流,返回实际写入个数,小于nr出错
3.5 定位/清洗流
#include <stdio.h>
int fseek(FILE *stream, long offset, int whence);
//成功返回0,清空文件结束标志,取消ungetc()操作,错误返回-1
- SEEK_SET ,将该文件偏移量设置为距文件开始处
offset
个字节 - SEEK_CUR ,将该文件偏移量设置为当前值+
offset
,offset
可正可负 - SEEK_END ,将该文件偏移量设置为文件长度+
offset
,offset
可正可负
//将流位置设置到pos处
int fsetpos(FILE *stream, const fpos_t *pos);
//成功返回0,否则返回-1
//将当前位置填入pos
int fegtpos(FILE *stream, fpos_t *pos);
void rewind(FILE *stream);
//等于 fseek(stream,0,SEEK_SET); 位置重置为初始位置
//返回当前流位置
long fetll(FILE *stream);
//错误返回-1
//清洗一个流,流中数据立即刷新到内核中
int fflush(FILE *stream);
3.6 错误和文件结束
#include <stdio.h>
//测试流上是否设置了错误标志
int ferror(FILE *stream);
//如果有错误标记,返回非0,否则返回0
//测试文件结尾标记是否被设置
int feof(FILE *stream);
//如果有文件结束标记,返回非0,否则返回0
//清空错误和结尾标记
void cleareer(FILE *stream);
3.7 控制缓冲和关联描述符
- 提供三种用户缓冲,不缓冲,行缓冲(标准输出默认),块缓冲(文件操作默认)
#include <stdio.h>
int setvbuf(FILE *stream, char *buf, int mode, size_t size);
//必须在流打开后,任何操作之前调用
//成功返回0,否则返回非0
-
mode
-
_IONBF
无缓冲 -
_IOLBF
行缓冲 -
_IOFBUF
块缓冲
-
_IONBF
情况下buf和size被忽略buf可以指向一个size字节大小的缓冲空间,如果buf为NULL,则由glibc自动分配
获得关联文件的描述符
#include <stdio.h>
int fileno(FILE *stream);
//成功返回和流关联的文件描述符,否则返回-1
3.8 线程安全
单个I/O操作是原子的,但有时需要加锁一组操作
又时有不需要单个I/O的锁机制
手动文件加锁
#include <stdio.h>
void flockfile(FILE *stream);
//等待流解锁 并 获得锁 然后返回
vodi funlockfile(FILE *stream); //解锁
//非阻塞加锁版本
int ftrylockfile(FILE *stream);
//没有拿到锁返回非零,拿到了返回0
- 不加锁的流操作,linux提供一系列函数
#include <stdio.h>
int getc_unlocked(FILE *stream);
int getchar_unlocked(void);
int putc_unlocked(int c, FILE *stream);
int putchar_unlocked(int c);
void clearerr_unlocked(FILE *stream);
int feof_unlocked(FILE *stream);
int ferror_unlocked(FILE *stream);
int fileno_unlocked(FILE *stream);
int fflush_unlocked(FILE *stream);
int fgetc_unlocked(FILE *stream);
int fputc_unlocked(int c, FILE *stream);
size_t fread_unlocked(void *ptr, size_t size, size_t n,
FILE *stream);
size_t fwrite_unlocked(const void *ptr, size_t size, size_t n,
FILE *stream);
char *fgets_unlocked(char *s, int n, FILE *stream);
int fputs_unlocked(const char *s, FILE *stream);
...