目录
- 1 文件的概念
- 2 程序文件和数据文件
- 3 二进制文件和文本文件
- 4 流
- 4.1 流的概念
- 4.2 标准流
- 5 文件信息区和文件指针
- 6 处理文件的库函数
- 6.1 fopen
- 6.2 fclose
- 6.3 fgetc
- 6.4 fputc
- 6.5 fgets
- 6.6 fputs
- 6.7 fscanf
- 6.8 fprintf
- 6.9 fread
- 6.10 fwrite
- 6.11 fseek
- 6.12 ftell
- 6.13 rewind
- 6.14 feof 和 ferror
- 7 文件缓冲区
1 文件的概念
在磁盘中,用于保存数据的工具就是文件
比如,下图的 test.txt 就是一个文件,保存了 1 2 3 4 5 6 7 8 9 10 这样的数据
2 程序文件和数据文件
从程序设计的角度上来说,文件主要分为程序文件和数据文件
程序文件:用于保存程序代码的文件,比如.c后缀的源文件,.h后缀的头文件
数据文件:用于保存其它数据的文件,比如上文提到的 test.txt 文件
3 二进制文件和文本文件
在C语言编程中,对文件进行操作时,主要有二进制文件和文本文件两个对象,它们都属于数据文件
文本文件:数据在保存到文件时,会先将该数据按字节为单位转换为字符,再以ASCII码的形式保存
二进制文件:数据在保存到文件时,会直接以二进制序列的形式保存
举例:1000在文本文件和二进制文件中的不同保存形式
4 流
4.1 流的概念
由于计算机在运行时,会与各种各样的外部设备交流,所以这会使得程序员需要掌握这些设备的输入输出方式,对程序员的要求大大提高,为避免这种现象的发生,提出了流的概念,流向程序员提供了一种便捷的输入输出方式,程序员只需要通过流,就可以直接与外部设备进行交互,不需要关心具体输入输出的细节
4.2 标准流
标准流主要有3个,分别是 stdin,stdout,stderr
stdin:标准输入流,用来从键盘上读取数据,scanf 所使用的流就是它
stdout:标准输出流,用来向屏幕输出数据,printf 所使用的流就是它
stderr:标准错误流,用来在屏幕上显示错误信息
C语言的程序启动时,默认就开启了这三个流
5 文件信息区和文件指针
打开文件时,会在内存中生成一个文件信息区,文件信息区本质是一个结构体(名为FILE),保存了文件的各种信息
用户可以通过指向文件信息区的文件指针来访问对应的文件,文件指针的类型为FILE*
6 处理文件的库函数
6.1 fopen
fopen 的函数声明如下:
FILE* fopen (const char* filename, const char* mode);
功能
打开一个文件
参数
filename 指要打开的文件的名称
mode 指文件的打开方式
返回值
若文件打开成功,则会返回指向该文件文件信息区的文件指针
若文件打开失败,则会返回空指针
文件的打开方式
值 | 功能 | 文件不存在时 |
---|---|---|
“r” | 指定本次打开要进行 读文件 操作,对象为文本文件 | 出错 |
“w” | 指定本次打开要进行 写文件 操作并清空文件数据,对象为文本文件 | 创建新文件 |
“a” | 在文件末尾添加新内容,对象为文本文件 | 创建新文件 |
“rb” | 指定本次打开要进行 读文件 操作,对象为二进制文件 | 出错 |
“wb” | 指定本次打开要进行 读文件 操作,对象为二进制文件 | 创建新文件 |
“ab” | 在文件末尾添加新内容,对象为二进制文件 | 创建新文件 |
“r+” | 指定本次打开要进行 读文件和写文件 操作,对象为文本文件 | 出错 |
“w+” | 指定本次打开要进行 读文件和写文件 操作,对象为文本文件 | 创建新文件 |
“a+” | 在文件末尾添加新内容,对象为文本文件 | 创建新文件 |
“rb+” | 指定本次打开要进行 读文件和写文件 操作,对象为二进制文件 | 出错 |
“wb+” | 指定本次打开要进行 读文件和写文件 操作,对象为二进制文件 | 创建新文件 |
“ab+” | 在文件末尾添加新内容,对象为二进制文件 | 创建新文件 |
举例:以读的形式,打开一个名为 “test.txt” 的文本文件
#include <stdio.h>
int main()
{FILE* pf = fopen("test.txt", "r"); //以读的方式打开文件if (pf == NULL) //防止使用空指针assert(pf);fclose(pf); //关闭文件pf = NULL;return 0;
}
6.2 fclose
int fclose ( FILE * stream );
功能
用于关闭打开的文件
参数
stream 为指向要关闭文件的文件指针
6.3 fgetc
int fgetc ( FILE * stream );
功能
用来从文件中获取一个字符
参数
stream 为指向要读取文件的文件指针
返回值
若读取成功,返回读取到的字符
若读取失败或者遇到文件末尾,返回EOF
举例:从文件中读取一个字符并输出
#include <stdio.h>
#include <assert.h>
int main()
{FILE* pf = fopen("test.txt", "r");if (pf == NULL)assert(pf);printf("%c", (char)fgetc(pf));fclose(pf);pf = NULL;return 0;
}
6.4 fputc
int fputc ( int character, FILE * stream );
功能
用来向文件中写入字符
参数
character 为要写入的字符
stream 为指向要操作的文件的文件指针
返回值
若操作成功,则返回写入的字符
若操作失败,则返回EOF
举例:写一个字符到文件内
#include <stdio.h>
#include <assert.h>
int main()
{FILE* pf = fopen("test.txt", "w");if (pf == NULL)assert(pf);fputc('a', pf);fclose(pf);pf = NULL;return 0;
}
6.5 fgets
char * fgets ( char * str, int num, FILE * stream );
功能
用来从文件中读出 num-1 个字符,放入 str 指向的字符串内,最后自动补 ‘\0’
参数
str 为指向存放字符的字符串的指针
num 为要读的字符数量,实际读取数量为 num-1
stream 为指向要读的文件的文件指针
返回值
若操作成功,则返回存放读取字符的字符串首地址
若操作失败,则返回空指针
举例:读取文件中的 4 个字符到字符串内
#include <stdio.h>
#include <assert.h>
int main()
{FILE* pf = fopen("test.txt", "r");if (pf == NULL)assert(pf);char str[5] = " ";fgets(str, 5, pf);fclose(pf);pf = NULL;return 0;
}
6.6 fputs
int fputs ( const char * str, FILE * stream );
功能
将字符串的内容写到文件中
参数
str 为指向字符串的指针
stream 为指向要写的文件的指针
返回值
操作成功时,返回非负值
操作失败时,返回EOF
举例:读取一个字符串到文件内
#include <stdio.h>
#include <assert.h>
int main()
{FILE* pf = fopen("test.txt", "w");if (pf == NULL)assert(pf);char str[20] = "hello world";fputs(str, pf);fclose(pf);pf = NULL;return 0;
}
6.7 fscanf
int fscanf ( FILE * stream, const char * format, parameterList );
功能
根据给出的格式,从文件中读取内容到后方的变量中
参数
stream 为指向要读的文件的文件指针
format 为需要用户指定的格式,比如%d,%s等
paramterList 为要存放数据的变量,可以有1个或多个
返回值
返回成功读取的数据的量
如果在读取的中途出错或遇到文件尾,会设置 ferror 或 feof 指示器
如果一个数据都没有读取成功,返回EOF
举例:读取文件中的数据到变量中
#include <stdio.h>
#include <assert.h>
int main()
{FILE* pf = fopen("test.txt", "w");if (pf == NULL)assert(pf);char str[20] = " ";fscanf(pf, "%s", str);fclose(pf);pf = NULL;return 0;
}
6.8 fprintf
int fprintf ( FILE * stream, const char * format, parameterList);
功能
根据给出的格式,将对应变量中的值写入文件中
参数
stream 为指向要写的文件的指针
format 为需要用户指定的格式,比如%d,%s等
paramterList 为要读取的变量,可以有1个或多个
返回值
操作成功,则返回成功写入的字符的数量
操作失败,返回负数
举例:写入数据到文件中
#include <stdio.h>
#include <assert.h>
int main()
{FILE* pf = fopen("test.txt", "w");if (pf == NULL)assert(pf);char str[20] = "hello world";printf("%d", fprintf(pf, "%s", str));fclose(pf);pf = NULL;return 0;
}
6.9 fread
size_t fread ( void * ptr, size_t size, size_t count, FILE * stream );
功能
从文件中读取 count 个 size 大小的数据到 ptr 指向的空间内,但是是以二进制的形式进行读取
参数
ptr 为指向最终存放数据的空间的指针
size 是读取的每个元素的大小,单位为字节
count 是读取的元素个数
stream 是指向要读取的文件的文件指针
返回值
返回成功读取的元素的个数
举例:从文件中读取5个4字节的数据至数组中
#include <stdio.h>
#include <assert.h>
int main()
{FILE* pf = fopen("test.txt", "rb");if (pf == NULL)assert(pf);int arr[5] = { 0 };fread(arr, sizeof(int), 5, pf);fclose(pf);pf = NULL;return 0;
}
6.10 fwrite
size_t fwrite ( const void * ptr, size_t size, size_t count, FILE * stream );
功能
从 ptr 指向的空间内读取 count 个 size 大小的数据并以二进制的形式写到文件内,
参数
ptr 为指向要读取的空间的指针
size 每个元素的大小
count 为元素的个数
stream 为指向要写入数据的文件的指针
返回值
返回成功写入的元素的个数
举例:向文件中写入5个4字节的数据
#include <stdio.h>
#include <assert.h>
int main()
{FILE* pf = fopen("test.txt", "wb");if (pf == NULL)assert(pf);int arr[5] = { 1,2,3,4,5 };fwrite(arr, sizeof(int), 5, pf);fclose(pf);pf = NULL;return 0;
}
6.11 fseek
int fseek ( FILE * stream, long int offset, int origin );
功能
通过文件指针的位置,偏移量来移动文件指针
参数
stream 是指向文件的文件指针
offset 是偏移量,用来计算新的文件指针位置
origin 是文件指针的位置,有以下三个取值
取值 | 含义 |
---|---|
SEEK_SET | 文件的起始位置 |
SEEK_CUR | 当前文件指针的位置 |
SEEK_END | 文件的末尾 |
返回值
操作成功,返回0
操作失败,返回非0
举例:读取下面这个文件的d字符
#include <stdio.h>
#include <assert.h>
int main()
{FILE* pf = fopen("test.txt", "r");if (pf == NULL)assert(pf);fseek(pf, 3, SEEK_CUR);printf("%c", fgetc(pf));fclose(pf);pf = NULL;return 0;
}
图解:
注意事项
若 origin 取值为 SEEK_END ,则偏移量需要给入负数,才能读取前面的值
6.12 ftell
long int ftell ( FILE * stream );
功能
返回当前文件指针相对于起始位置的偏移量
参数
stream 为指向文件的文件指针
返回值
成功返回当前文件指针的位置
错误返回 -1L
举例:计算下面文件的文件尾相对于起始位置的偏移量
#include <stdio.h>
#include <assert.h>
int main()
{FILE* pf = fopen("test.txt", "r");if (pf == NULL)assert(pf);while (fgetc(pf) != EOF); //使文件指针遍历到结尾printf("%d", ftell(pf));fclose(pf);pf = NULL;return 0;
}
图解:
6.13 rewind
void rewind ( FILE * stream );
功能
将文件指针重置到文件的开始位置
参数
stream 为指向文件的文件指针
返回值
无
举例:输出两次下列文件的内容
#include <stdio.h>
#include <assert.h>
int main()
{FILE* pf = fopen("test.txt", "r");if (pf == NULL)assert(pf);char ch = 0;while ((ch = fgetc(pf)) != EOF){printf("%c ", ch);}printf("\n", ch);rewind(pf); //重置文件指针的位置while ((ch = fgetc(pf)) != EOF){printf("%c ", ch);}fclose(pf);pf = NULL;return 0;
}
6.14 feof 和 ferror
int feof ( FILE * stream );
功能
判断文件的读取是否因为遇到了文件尾而结束
参数
stream 为指向文件的文件指针
返回值
是遇到了文件尾而结束,则返回非0
不是则返回0
int ferror ( FILE * stream );
功能
判断文件的读取是否因为遇到了错误而结束
参数
stream 为指向文件的文件指针
返回值
是遇到了错误而结束则返回非0
不是则返回0
我们可以通过使用 feof 和 ferror 来让文件的读取更加安全
#include <stdio.h>
#include <assert.h>
int main()
{FILE* pf = fopen("test.txt", "r");if (pf == NULL)assert(pf);char ch = 0;while ((ch = fgetc(pf)) != EOF){printf("%c ", ch);}if (feof(pf)){printf("遇到文件尾结束");}else if (ferror(pf)){perror("fault");}fclose(pf);pf = NULL;return 0;
}
7 文件缓冲区
计算机在从文件内读取数据或向文件内写入数据时,会先将数据存入输入/输出缓冲区,待输入/输出缓冲区被填满时,才会将数据读入计算机或输入到文件内,如果没有缓冲区的话,那么就会出现输入/输出一次数据,操作系统就要介入进行处理的情况,操作系统不断被打断,无法服务于其它的进程,会使得操作系统的效率降低