目录
C语言是不仅可以处理内存中的数据,还可以与外部文件进行交互。以下是C语言中的文件操作,包括文件的创建、打开、读取和写入等基本操作。
什么是文件:
磁盘上的文件就是文件。例如电脑当中的C盘内放入的文件夹内的内容就是文件。但是在程序设计中,我们一般谈的文件有两种:程序文件、数据文件(从文件功能的角度来分类的)。
- 程序文件: 包括源程序文件(后缀为.c),目标文件(windows环境后缀为.obj),可执行程序(windows环境后缀为.exe)。程序文件是开发人员编写代码的主要工作区域。编译器会将程序文件编译为可执行文件,以便在计算机上执行。
- 数据文件: 数据文件是用于存储和读取数据的文件,文件的内容不一定是程序,而是程序运行时读写的数据。
文件的创建:
文件的创建 在C语言中,可以使用标准库函数fopen()
来创建或打开一个文件。fopen()
函数需要两个参数:文件名和打开模式。
*****打开模式:
1."r" 模式:
1.1 打开文件进行“只读”操作,即只能从文件读取内容。
1.2 若欲操作的文件不存在,则打开失败。
1.3 成功打开文件时,文件指针位于文件开头。
1.4 打开文件后,不会清空文件内原有内容。
1.5 可从文件中任意位置读取内容。
2."w" 模式:
2.1 打开文件进行“只写”操作,即只能向文件写入内容。
2.2 若欲操作的文件不存在,则新建文件。
2.3 成功打开文件时,文件指针位于文件开头。
2.4 打开文件后,会清空文件内原有的内容。
2.5 可向文件中任意位置写入内容,且进行写入操作时,会覆盖原有位置的内容。
3."a" 模式:
3.1 打开文件进行“追加”操作,即只能向文件写入内容。
3.2 若欲操作的文件不存在,则新建文件。
3.3 成功打开文件时,文件指针位于文件结尾。
3.4 打开文件后,不会清空文件内原有内容。
3.5 只能向文件末尾追加(写)内容。
4."r+"模式:
4.1 打开文件进行“读写”操作,即既可读取,又可写入。
4.2 若欲操作的文件不存在,则打开失败。
4.3 成功打开文件时,文件指针位于文件开头。
4.4 打开文件后,不会清空文件内原有内容。
4.5 无论是读取内容还是写入内容,都可在文件中任意位置进行,且进行写入操作时,会 覆盖原有位置的内容。
5."w+"模式:
5.1 打开文件进行“读写”操作,即既可读取,又可写入。
5.2 若欲操作的文件不存在,则新建文件。
5.3 成功打开文件时,文件指针位于文件开头。
5.4 打开文件后,会清空文件内原有的内容。
5.5 无论是读取内容还是写入内容,都可在文件中任意位置进行,且进行写入操作时,会 覆盖原有位置的内容。
6."a+"模式:
6.1 打开文件进行“读写”操作,即既可读取,又可写入。
6.2 若欲操作的文件不存在,则新建文件。
6.3 成功打开文件时,文件指针位于文件结尾。
6.4 打开文件后,不会清空文件内原有内容。
6.5 读取内容时,可以在任意位置进行,但写入内容时,只会追加在文件尾部。
********下面是一个创建文件的示例:
#include <stdio.h>
int main() {
FILE *file = fopen("test.txt", "w");
if (file == NULL) {
printf("无法创建文件\n");
return 1;
}
printf("文件创建成功\n");
fclose(file);
return 0;
}
文件的关闭:
文件的打开和关闭 在C语言中,可以使用fopen()
函数打开已存在的文件。打开文件后,需要使用fclose()
函数关闭文件,以释放资源。
********下面是一个打开和关闭文件的示例:
#include <stdio.h>
int main() {
FILE *file = fopen("example.txt", "r");
if (file == NULL) {
printf("无法打开文件\n");
return 1;
}
printf("文件打开成功\n");
fclose(file);
return 0;
}
文件的读取:
在C语言中,可以使用fscanf()
函数从文件中读取数据。fscanf()
函数需要三个参数:文件指针、格式化字符串和要读取的变量。
********下面是一个从文件中读取数据的示例:
#include <stdio.h>
int main() {
FILE *file = fopen("test.txt", "r");
if (file == NULL) {
printf("无法打开文件\n");
return 1;
}
int num;
fscanf(file, "%d", &num);
printf("从文件中读取的数字是:%d\n", num);
fclose(file);
return 0;
}
文件的写入:
在C语言中,可以使用fprintf()
函数将数据写入文件。fprintf()
函数需要两个参数:文件指针和要写入的内容。
********下面是一个将数据写入文件的示例:
#include <stdio.h>
int main() {
FILE *file = fopen("test.txt", "w");
if (file == NULL) {
printf("无法打开文件\n");
return 1;
}
int num = 10;
fprintf(file, "%d", num);
printf("数据写入文件成功\n");
fclose(file);
return 0;
}
功能 | 函数名 | 适用于 |
---|---|---|
字符输入函数 | fgetc | 所有输入流 |
字符输出函数 | fputc | 所有输出流 |
文本行输入函数 | fgets | 所有输入流 |
文本行输出函数 | fputs | 所有输出流 |
格式化输入函数 | fscanf | 所有输入流 |
格式化输出函数 | fprintf | 所有输出流 |
二进制输入 | fread | 文件 |
二进制输出 | fwrite | 文件 |
文件的顺序读写:
fseek函数:
********原型:
int fseek(FILE *stream, long offset, int origin);
********参数说明:
stream
:指向要操作的文件的指针。offset
:要移动的字节数或记录数,可以是正数、负数或零。origin
:指定起始位置,可以是以下值之一:SEEK_SET
:从文件开头开始计算偏移量。SEEK_CUR
:从当前位置开始计算偏移量。SEEK_END
:从文件末尾开始计算偏移量。
fseek
函数的返回值为0表示成功,非零值表示出现错误。
********使用fseek
函数可以实现的操作:
- 定位到文件的开头、末尾或当前位置。
- 跳过指定数量的字节或记录。
- 在文件中定位到特定的字节或记录。
********示例:
#include <stdio.h>
int main()
{
FILE *file = fopen("data.txt", "r");
if (file == NULL)
{
printf("Failed to open the file.\n");
return 1;
}
// 将文件指针定位到文件末尾
fseek(file, 0, SEEK_END);
// 获取文件大小
long size = ftell(file);
printf("File size: %ld bytes\n", size);
// 将文件指针定位到文件开头
fseek(file, 0, SEEK_SET);
// 读取文件内容
char buffer[100];
fgets(buffer, sizeof(buffer), file);
printf("First line: %s\n", buffer);
fclose(file);
return 0;
}
运行结果为:Failed to open the file.
ftell函数:
********原型:
long ftell(FILE *stream);
********参数说明:
stream
:指向要获取位置的文件的指针。
ftell
函数返回一个long
类型的值,表示当前文件指针的偏移量。如果发生错误,返回值为-1。
示例:
#include <stdio.h>
int main()
{
FILE *file = fopen("data.txt", "r");
if (file == NULL) {
printf("Failed to open the file.\n");
return 1;
}
// 获取文件指针的当前位置
long position = ftell(file);
printf("Current position: %ld\n", position);
fclose(file);
return 0;
}
上述示例中,首先打开一个文件,并使用ftell
函数获取文件指针的当前位置。然后将当前位置打印到控制台上。
rewind函数:
********原型:
void rewind(FILE *stream);
********参数说明:
stream
:指向要重新定位的文件的指针。
rewind
函数不返回任何值。
********示例:
#include <stdio.h>
int main()
{
FILE *file = fopen("data.txt", "r");
if (file == NULL) {
printf("Failed to open the file.\n");
return 1;
}
// 将文件指针重新定位到文件开头
rewind(file);
// 读取文件内容
char buffer[100];
fgets(buffer, sizeof(buffer), file);
printf("First line: %s\n", buffer);
fclose(file);
return 0;
}
上述示例中,首先打开一个文件,并使用rewind
函数将文件指针重新定位到文件开头。然后使用fgets
函数读取文件的第一行内容,并将其打印到控制台上。
文本文件和二进制文件:
根据数据的组织形式,数据文件被称为文本文件或者二进制文件。
数据在内存中以二进制的形式存储,如果不加转换的输出到外存,就是二进制文件。
如果要求在外存上以ASCII码的形式存储,则需要在存储前转换。以ASCII字符的形式存储的文件就是文本文件。(如整数10000,需要以ASCII码输出到磁盘上,则在磁盘中的存储形式就是10000)。
如有整数10000,如果以ASCII码的形式输出到磁盘,则磁盘中占用5个字节(每个字符一个字节),而如果以二进制形式输出,则在磁盘上只占4个字节。
文件读取结束的判定:
feof函数的错误用法:
在C语言中,feof
函数用于检测文件流的结束标志。它用于判断是否已经达到文件的末尾。然而,feof
函数容易被误用,特别是在循环读取文件时。以下是一些常见的错误使用feof
函数的情况:
错误使用方式一:在循环读取文件时错误地使用feof
函数作为循环条件。
FILE *file = fopen("data.txt", "r");
if (file == NULL)
{
printf("Failed to open the file.\n");
return 1;
}
while (!feof(file))
{
// 读取文件内容
// ...
}
问题:feof
函数在文件末尾之前不会返回真值,而是在文件末尾时才返回真值。因此,循环将尝试读取文件的最后一行两次,导致错误。
正确的方式:在读取文件内容后立即检查读取操作的结果,而不是依赖于feof
函数作为循环条件。
错误使用方式二:仅依赖feof
函数判断文件结束,而不检查读取操作的结果。
FILE *file = fopen("data.txt", "r");
if (file == NULL)
{
printf("Failed to open the file.\n");
return 1;
}
while (!feof(file))
{
char buffer[100];
fgets(buffer, sizeof(buffer), file);
printf("%s\n", buffer);
}
问题:feof
函数只能判断文件是否已到达末尾,但不能检查读取操作是否成功。如果在读取操作失败后调用feof
函数,它仍然会返回假值,导致错误的处理。
正确的方式:在读取文件内容后,检查读取操作的结果,例如使用fgets
函数返回的指针是否为NULL,来判断是否成功读取一行内容。
文件缓冲区:
说到文件缓冲区,我们就自然而然想到输入缓冲区,即当一个字符一个字符从键盘上输入时,并不是直接输入到磁盘内,而是先放到输入缓冲区,而当输入缓冲区内的字符放满后,文件缓冲区才向磁盘内输入字符。
文件缓冲区也是一样的道理。从内存向磁盘输出数据会先送到内存中的缓冲区,装满缓冲区后才一起送到磁盘上。如果从磁盘向计算机读入数据,则从磁盘文件中读取数据输入到内存缓冲区(充满缓冲区),然后再从缓冲区逐个地将数据送到程序数据区(程序变量等)。缓冲区的大小根据C编译系统决定的。
#include <stdio.h>
#include <windows.h>
//vs2022 win11 编译环境
int main()
{
FILE*pf = fopen("test.txt", "w");
fputs("abcdef", pf);//先将代码放在输出缓冲区
printf("睡眠10秒-已经写数据了,打开test.txt文件,发现文件没有内容\n");
Sleep(10000);
printf("刷新缓冲区\n");
fflush(pf);//刷新缓冲区时,才将输出缓冲区的数据写到文件(磁盘)
printf("再睡眠10秒-此时,再次打开test.txt文件,文件有内容了\n");
Sleep(10000);
fclose(pf);
//注:fclose在关闭文件的时候,也会刷新缓冲区
pf = NULL;
return 0; }
测试一下这个代码,在程序第一个到fgets函数处时,立刻去打开test.txt文本文件,我们会发现里面没有内容,而我们用刷新文件缓冲区的fflush函数时再次打开test.txt文本文件时,会发现里面已经有输入的内容。则能够证实的确有文件缓冲区的存在。
因为有缓冲区的存在,C语言在操作文件的时候,需要做刷新缓冲区或者在文件操作结束的时候关闭文件。如果不做,可能导致读写文件的问题。