第十三章 文件输入/输出
13.1 与文件进行通信
文件(file
):通常是在磁盘或者固态硬盘上的一段已命名的存储区。
文本模式和二进制模式
UNIX
使用同一种文件格式处理文本文件和二进制文件,其他系统可能使用其他方式处理文本文件。主要区别在:
换行标志:
OS X Macintosh ”\r“
,MS-DOS “\r\n”
,UNIX "\n"
文件尾标志:
MS-DOS “ctrl+z“
,UNIX
文件大小计数是否要保持每行长度对齐
为了规范文本文件处理,C
提供了两种访问文件的途径:文本模式和二进制模式。
二进制模式:可以访问文件的每个字节。
文本模式:将文本文件转化为
C
模式,即UNIX
的处理方式。
底层 I/O
(系统 I/O
):使用系统提供的 I/O
服务;
标准高级 I/O
:使用 C 库的标准包和 stdio.h
头文件定义。
标准输入(standard input
):stdin
,一般为键盘
标准输出(standard output
):stdout
,一般为屏幕
标准错误输出(standard error output
):stderr
,一般为屏幕,不受重定向控制。
13.2 标准 I/O
使用标准 I/O
的好处
有许多专门的函数简化了处理不同
I/O
的问题。输入输出都是缓冲的,极大提高了传输效率。
FILE* fopen ( const char* filename, const char* mode )
模式字符串 | 含义 |
---|---|
”r“ | 读模式打开;文件必须存在 |
”w“ | 写模式打开,创建文件,如果文件存在则清空它。 |
”a“ | 附加模式打开,创建文件,如果文件不存在则创建。 |
”r+“ | 读/更新模式打开,文件必须存在。 |
”w+“ | 写/更新模式打开,创建文件,如果文件存在则清空它。 |
”a+“ | 附加更新模式打开,创建文件,如果文件存在则清空它。 |
”b“ | 二进制模式打开,可以与上述任意一个模式组合。 |
”x“ |
(C11) 只能与写模式共用,如果文件已存在或以独占模式打开则失败。 |
说明
读模式 r:可读,文件必须存在;
写模式 w:可写,创建文件,若存在则清空;
更新模式 +:可读可写;
附加模式 a:在文件末尾,创建文件,若存在则清空,且重定位写操作不生效,读生效;
总结:读模式要文件存在,写,附加模式会创建文件;更新模式只定义权限。
Linux/UNIX
只有一种文件类型,带不带'b'
都没区别。
getc/putc
比 getchar/putchar
多一个流的控制。
getchar('a') = getc('a', stdin)
,putchar('a') = putc('a', stdout)
EOF
表示文件尾
int fclose ( FILE* stream )
关闭打开的文件
13.3 一个简单的文件压缩程序
演示了标准 I/O
操作
13.4 文件 I/O:fprintf()、fscanf()、fgets() 和 fputs()
int fprintf ( FILE* stream, const char* format, ... )
int fscanf ( FILE* stream, const char* format, ... )
类似 printf/scanf
,多了流控制
char * fgets ( char* str, int num, FILE* stream )
int fputs ( const char* str, FILE* stream )
前面讲过
13.5 随机访问:fseek() 和 fell()
int fseek ( FILE* stream, long int offset, int origin )
模式 | 偏移量的起始点 |
---|---|
SEEK_SET | 文件开始处 |
SEEK_CUR | 当前位置 |
SEEK_END | 文件末尾 |
fseek(fp, 0L, SEEK SET); // 定位至文件开始处
fseek(fp, 10L, SEEK SET); // 定位至文件中的第 10 个字节
fseek(fp, 2L, SEEK CUR); //从文件当前位置前移 2 个字节
fseek(fp, 0L, SEEK END); // 定位至文件结尾
fseek(fp, -10L, SEEK END); //从文件结尾处回退 10个字节
如果一切正常返回 0
,如果出现错误(试图移动到文件范围外),返回 -1
。
long int ftell ( FILE* stream )
返回距当前位置文件开始处的位置偏移
/* 与 fseek 配合获取文件大小 */
FILE *fp = fopen("path_of_file", "rb+");
fseek(fp, 0L, SEEK_END);
long int file_size = ftell(fp);
鉴于各个系统的差异,
fseek(),ftell()
无法做到与UNIX
模型保持一致。因此ANSI
对这些函数降低了要求:
在二进制模式中,实现不必支持
SEEK_END
模式。移植性更高的方法是逐字节读取整个文件直到文件末尾。文本模式,保证以下行为
函数调用 效果 fseek(fp, 0L, SEEK_SET) 定位至文件开始处 fseek(fp, 0L, SEEK_CUR) 保持当前位置不动 fseek(fp, 0L, SEEK_END) 定位至文件结尾 fseek(fp, ftell-pos, SEEK_SET) 到距文件开始处 ftell-pos 的位置,ftell - pos 是 ftell() 的返回值
int fgetpos ( FILE* stream, fpos_t* pos )
int fsetpos ( FILE* stream, const fpos_t* pos )
解决 fseek(),ftell()
文件大小限制在 long
类型的问题,fpos_t
具体实现取决与平台。
13.6 标准 I/O 的机理
- 打开文件流
- 初始化结构和缓冲区
- 输入/输出函数从缓冲区读取/写入数据,缓冲区满时,更新缓冲区。
13.7 其他标准 I/O 函数
int ungetc(int c, FILE* fp)
把字符放回输入流中。
*int fflush(FILE fp)
将输出缓冲区中所有未写入的数据发送到 fp
指定的输出文件。
int setvbuf(FILE restrict fp, char restrict buf, int mode, size_t size)**
创建一个供标准 I/O 函数使用的缓冲区。
mode
_IOFBF:全缓冲;
_IONBF:无缓冲。
size_t fread ( void* ptr, size_t size, size_t count, FILE* stream )
size_t fwrite ( const void* ptr, size_t size, size_t count, FILE* stream )
处理以二进制形式存储的文件
int feof(FILE* fp)
当上一次输入调用检测到文件结尾时,返回非零。
int ferror(FILE* fp)
当读/写错误时返回非零。