第 13 章 文件输入/输出
第十三章 文件输入/输出¶
👉【复习题】【编程练习题】¶
文件用于 存储程序、文档、数据、书信、表格、照片、视频
等。
1. 与文件进行通信¶
文件:通常是在磁盘或固态硬盘上的一段已命名的存储区。
C语言提供两种文件模式:文本模式
和 二进制模式
。
所有文件的内容都以二进制形式(0或1)
存储。
2. 标准I/O¶
C程序会自动打开3个文件:标准输入(standard input)
、标准输出(standard ouput)
、标准错误输出(standard error output)
。
标准文件 | 文件指针 | 通常使用的设备 |
---|---|---|
标准输入 | stdin | 键盘 |
标准输出 | stdout | 显示器 |
标准错误 | stderr | 显示器 |
标准I/O的好处: - 可移植 - 有专门的函数简化了处理不同I/O的问题 - 输入和输出都是缓冲(缓冲极大提高数据传输速率)。
在打开文件时,程序一定要判断文件是否打开成功。
一旦打开失败,直接会终止后续操作。
exit() 函数
关闭所有打开的文件并结束程序。
在stdlib.h头文件中:
标准要求
0
或 宏EXIT_SUCCESS
:表明成功结束程序。宏
EXIT_FAILURE
:表明结束程序失败。
2.1 fopen()
和 fclose()
函数¶
都声明在 stdlib.h
头文件中。
函数 | 函数原型 | 语法格式 | 功能 | 备注 |
---|---|---|---|---|
fopen() |
FILE *fopen(const char *filename,const char * mode); |
FILE *fp = fopen("filename",mode) |
打开文件 | 返回一个文件指针:FILE *fp 指向一个记录文件信息的数据结构 例: fp = fopen("hello_c.txt","r"); |
fclose() |
int fclose(FILE * stream); |
fclose(fp) |
关闭文件 | 关闭成功返回0,失败返回EOF(-1),存储空间不足或者被移除都会出现I/O错误,都会导致失败。 |
文件指针的类型是指向FILE的指针,FILE是一个定义在
stdlib.h
中的派生类型
。
mode的内容参考:
2.2 getc()
和 putc()
函数¶
与 getchar()
和 putchar()
函数类似。
区别:
需要告知
getc()
和putc()
函数 使用哪一个文件。
函数 | 函数原型 | 语法格式 | 功能 | 备注 |
---|---|---|---|---|
gets() |
int getc(FILE *stream) |
ch = getc(fp) |
从fp指定文件中获取一个字符,读到文件结尾返回EOF | getc(stdin) == getchar(ch); |
putc() |
int putc(int char,FILE *stream) |
putc(ch,fp) |
把ch放入fp指向文件 | puts(ch,stdout) == putchar(ch); |
2.3 一个简单的文件压缩程序¶
例子:把一个文件中选定的数据拷贝到另一个文件中。
fprintf()
和printf()
类似,⚠️注意点:fptrintf()
第一个参数必须是一个文件指针
。
2.4 标准I/O的原理¶
步骤:
- 调用fopen()
打开文件。
- fopen()
不仅打开一个文件,还创建了一个缓冲区(在读写模式下会创建两个缓冲区)以及一个包含文件和缓冲区数据的结构。
- fopen()返回一个指向该结构的指针,以便其他函数知道该如何找到该结构。
- 结构中通常包含一个指定流中当前未知的文件位置指示器。还包括错误和文件结尾的指示器、一个指向缓冲区开始处的指针,一个文件标识符和一个计数(统计实际拷贝进缓冲区的字节数)。
- 调用一个定义在
stdio.h
中的输入函数,如fscanf()、getc()或fgets()
。- 调用函数时,文件中的数据块会被拷贝到缓冲区中,最初调用函数,除了填充缓冲区外,还要设置指针变量所指向的结构中的值。
- 在初始化结构和缓冲区后,输入函数按要求从缓冲区中读取数据,在读取数据时,文件位置指示器被设置为指向刚读取字符的下一个字符。
- 当输入函数发现已读完缓冲区中的所有字符时,会请求把下一个缓冲大小的数据块从文件拷贝到该缓冲区中。
- 输出函数以类似的方式把数据写入缓冲区。当缓冲区被填满时,数据将被拷贝至文件中。
3. 文件I/O:fprintf()、fscanf()、fgets()、fputs()
¶
3.1 fprintf()
和 fscanf()
函数¶
fprintf()
和 fscanf()
函数的工作方式与 printf()
和 fscanf()
函数的类似。
👉小区别:
fprintf()
和fscanf()
函数的第一个参数必须指定待处理的文件
。
例子:
3.2 fgets()
和 fputs()
函数¶
移步 👉 【第11章】
4. 随机访问¶
4.1 fseek()
和 ftell()
函数¶
函数名 | 函数原型 | 功能 | 参数 | 返回值 |
---|---|---|---|---|
fseek() |
int fseek(FILE *stream, long int offset, int whence); |
重定位流上的文件指针 | 第一个参数stream为文件指针 第二个参数offset为 偏移量 ,正数表示正向偏移,负数表示负向偏移第三个参数是 模式 ,确定文件起始点,几种明示常量为:SEEK_SET(文件开头)、 SEEK_CUR(当前位置) 或 SEEK_END(文件末尾) |
如果成功,则该函数返回零,否则返回非零值。 |
ftell |
long ftell(FILE *stream); |
得到文件位置指针当前位置相对于文件首的偏移字节数。 | FILE *stream 返回指针的文件流 |
成功----返回当前文件指针的位置 出错----返回-1L,是长整数的-1值。 |
fseek
例子:
ftell
例子:
假设文件file.txt中的内容为
使用gcc运行程序后结果如下:
4.2 二进制模式和文本模式¶
不同之处:
- UNIX
- UNIX只有一种文件格式,所以不需要进行特殊的转换。
- UNIX使用 \n
表示换行符。
>以文本模式
打开时,C能识别Ctrl-Z
作为文件结尾标记的字符
。
>
>以二进制模式
打开相同的文件时,Ctrl-Z
字符被看作是文件中的一个字符
,而实际的文件结尾符在该字符后面。
- MS-DOS
- MS-DOS编译器都用 Ctrl + Z
标记文件。
- MS-DOS用 \r\n
组合表示文件换行符。
>以文本模式
打开相同的文件,C程序把\r\n
看成\n
,但是,以二进制模式打开该文件时,程序能看见这两个字符。
4.3 可移植性¶
C模型无法做到与Unix模型一致,因为历史上C就是因为Unix而生,但是其他系统不能保证与Unix模型一致。因此,ANSI对这些函数降低了要求,下面是一些限制:
- 在
二进制模式
中,实现不必支持SEEK_END模式
。 - 在
文本模式
中,只有以下调用能保证其相应的行为。
函数调用 效果 fseek(file,0L,SEEK_SET)
定位至文件开始处 fseek(file,0L,SEEK_CUR)
保持当前位置不动 fseek(file,0L,SEEK_END)
定位至文件结尾 fseek(file,ftell-pos,SEEK_SET)
到距文件开始处ftell-pos的位置,ftell-pos是ftell()的返回值
4.4 fgetpos()
和 fsetpos()
函数¶
- fgetpos() 函数
语法格式:
参数说明:
stream : 当前文件流的指针
fpos_t : 用来表示文件读写指针位置的类型,用来指明正在操作的文件中读或写的位置,文件头处为0。fpos_t在不同的平台下有不同的类型。
position : 指向 fpos_t 对象的指针
功能:处理较大文件(字节数超过long范围),解决
fseek()
和ftell()
函数存在的问题。返回值:执行成功时返回0,否则返回非0值。
ANSI C定义了如何使用 fpos_t
类型,fgetpos()
函数的原型如下:
fsetpos()
函数
语法格式:
调用函数时,使用pos指向位置上的
fpos_t
类型值来设置文件指针指向该值指定的位置。函数成功返回0,失败则返回非0。
5. 二进制I/O:fread()
和 fwrite()
¶
如果以程序所用的表示法把数据储存在文件中。则称以二进制形式存储数据
。
不存在从数值形式
到字符串
的转换过程。对于标准I/O,fread()
和 fwrite()
函数用于以二进制形式处理数据。
所有的数据都是以 二进制形式存储。
ANSI C 和许多OS都识别两种文件格式:二进制
和 文本
。
6. 其他I/O函数¶
一般都是成功返回0,不成功返回非零值:EOF(-1)
函数 | 函数原型 | 功能 |
---|---|---|
ungetc() |
int ungetc(int c, FILE* fp) |
把c指定的字符放回输入流中,如果把一个字符放回输入流,下次调用标准输入函数时将读取该字符。 |
fflush() |
int fflush(FILE *stream) |
调用函数引起输出缓冲区中所有的未写入数据被发送到stream指定的输出文件,该过程叫作 刷新缓冲区 。如果指针stream是空指针,所有输出缓冲区都被刷新。 |
setvbuf() |
int setvbuf(FILE* fp, char * buf, int mode, size_t size); |
创建一个提供I/O函数替换使用的缓冲区。 |
6.1 size_t fwrite()
函数¶
fwrite()
函数的原型:
指针ptr
:待写入数据块的地址。
size
:待写入数据块的大小(以字节为单位)。
nmemb
:待写入数据块的数量。
stream
:指定待写入的文件。fwrite()
函数返回成功写入项的数量。
6.2 size_t fread()
函数¶
fread()
函数的原型:
在
fread()
函数中,ptr
是待读取文件数据在内存中的地址。stream
指定待读取的文件。fread() 函数返回成功读取项的数量。
6.3 int feof(FILE *fp)
和 int ferror(FILE *fp)
函数¶
如果标准输入函数EOF,则通常表明函数已到达文件结尾。
feof()
函数返回一个非零值,否则返回0。
ferror()
函数返回一个非零值,否则返回0。
7. 用二进制I/O进行随机访问¶
总结¶
输入函数getc()
、fgets()
、fscanf()
和fread()
都是从文件开始处按顺序
读取文件。
fseek()
和 ftell()
函数让程序可以随机访问文件中的任意位置
。fgetpos()
和 fsetpos()
把类似的功能扩展到更大的文件。
与文本模式相比,二进制模式更容易进行随机访问。