欢迎来到C语言的文件操作教程!在这篇文章中,我们将探讨C语言中的文件操作,包括文本文件和二进制文件的处理,文件的打开和关闭,文件的顺序读写和随机读写,以及文件读取结束的判定和文件缓冲区的使用。
一、文本文件和二进制文件
在C语言中,文件被分为两类:文本文件和二进制文件。
-
文本文件是由字符组成的文件,可以使用文本编辑器打开并查看其内容。文本文件中的字符通常使用ASCII编码,每个字符占用一个字节。
-
二进制文件可以包含任意类型的数据,如整数、浮点数、结构体等。二进制文件中的字节是按照原始的二进制形式存储的,通常不使用任何编码。
二、文件的打开和关闭
在C语言中,我们使用fopen
函数来打开一个文件,使用fclose
函数来关闭一个已打开的文件。
FILE *fp;
fp = fopen("file.txt", "r"); // 打开一个名为"file.txt"的文件进行读取
if (fp == NULL) {
printf("无法打开文件\n");
return 1;
}
// 对文件进行操作
fclose(fp); // 关闭文件
三、文件的顺序读写
顺序读写是指按照文件中的数据顺序,从头到尾进行读写。在C语言中,我们可以使用fgetc
, fputc
, fgets
, fputs
, fscanf
, fprintf
等函数进行顺序读写。
1. 读取字符
fgetc
函数用于从文件中读取一个字符。它的原型是:
int fgetc(FILE *stream);
这个函数从stream
指向的文件中读取一个字符,并返回它。如果已经到达文件的末尾,或者发生了错误,它将返回EOF
。
FILE *fp = fopen("file.txt", "r");
if (fp == NULL) {
printf("无法打开文件\n");
return 1;
}
char ch;
while ((ch = fgetc(fp)) != EOF) {
putchar(ch);
}
fclose(fp);
2. 写入字符
fputc
函数用于向文件中写入一个字符。它的原型是:
int fputc(int c, FILE *stream);
这个函数将字符c
写入stream
指向的文件,并返回写入的字符。如果发生了错误,它将返回EOF
。
FILE *fp = fopen("file.txt", "w");
if (fp == NULL) {
printf("无法打开文件\n");
return 1;
}
char ch = 'A';
fputc(ch, fp);
fclose(fp);
3. 读取字符串
fgets
函数用于从文件中读取一个字符串。它的原型是:
char *fgets(char *str, int n, FILE *stream);
这个函数从stream
指向的文件中读取最多n-1
个字符,并将它们存储在str
指向的字符数组中。如果遇到换行符或文件的末尾,读取将停止。
FILE *fp = fopen("file.txt", "r");
if (fp == NULL) {
printf("无法打开文件\n");
return 1;
}
char str[100];
while (fgets(str, 100, fp) != NULL) {
printf("%s", str);
}
fclose(fp);
4. 写入字符串
fputs
函数用于向文件中写入一个字符串。它的原型是:
int fputs(const char *str, FILE *stream);
这个函数将str
指向的字符串写入stream
指向的文件。
FILE *fp = fopen("file.txt", "w");
if (fp == NULL) {
printf("无法打开文件\n");
return 1;
}
char *str = "Hello, World!";
fputs(str, fp);
fclose(fp);
5. 格式化读取和写入
fscanf
和fprintf
函数用于从文件中读取和写入格式化的数据。
FILE *fp = fopen("file.txt", "r");
if (fp == NULL) {
printf("无法打开文件\n");
return 1;
}
int num;
fscanf(fp, "%d", &num); // 读取一个整数
fclose(fp);
fp = fopen("file.txt", "w");
if (fp == NULL) {
printf("无法打开文件\n");
return 1;
}
fprintf(fp, "The number is %d\n", num); // 写入一个整数
fclose(fp);
以下是一个使用C语言格式化输入和输出结构体数据的例子:
首先,我们定义一个结构体Student
,包含name
,age
和grade
三个字段。
typedef struct {
char name[50];
int age;
float grade;
} Student;
然后,我们可以创建一个Student
类型的变量,并从文件中读取数据填充这个变量:
FILE *fp = fopen("student.txt", "r");
if (fp == NULL) {
printf("无法打开文件\n");
return 1;
}
Student s;
fscanf(fp, "%s %d %f", s.name, &s.age, &s.grade);
fclose(fp);
在这个例子中,我们假设student.txt
文件的内容如下:
ZhangSan 20 85.5
最后,我们可以将Student
类型的变量的数据写入到文件中:
FILE *fp = fopen("output.txt", "w");
if (fp == NULL) {
printf("无法打开文件\n");
return 1;
}
Student s = {"LiSi", 22, 90.0};
fprintf(fp, "%s %d %.2f", s.name, s.age, s.grade);
fclose(fp);
在这个例子中,output.txt
文件的内容将会是:
LiSi 22 90.00
四、文件的随机读写
随机读写是指可以在文件的任何位置开始读写。在C语言中,我们可以使用fseek
, ftell
和rewind
函数来改变文件指针的位置。
1. fseek函数
fseek
函数用于在文件中移动文件指针。它的原型是:
int fseek(FILE *stream, long offset, int whence);
这个函数将stream
指向的文件的文件指针移动offset
个字节。whence
参数决定了offset
的起始位置:
- 如果
whence
是SEEK_SET
,那么offset
是从文件的开头开始的。 - 如果
whence
是SEEK_CUR
,那么offset
是从当前位置开始的。 - 如果
whence
是SEEK_END
,那么offset
是从文件的末尾开始的。
如果成功,fseek
函数返回0。如果发生错误,它返回非零值。
FILE *fp = fopen("file.txt", "r");
if (fp == NULL) {
printf("无法打开文件\n");
return 1;
}
fseek(fp, 10, SEEK_SET); // 将文件指针移动到文件的第10个字节
fclose(fp);
2. ftell函数
ftell
函数用于获取当前文件指针的位置。它的原型是:
long ftell(FILE *stream);
这个函数返回stream
指向的文件的文件指针的当前位置。
FILE *fp = fopen("file.txt", "r");
if (fp == NULL) {
printf("无法打开文件\n");
return 1;
}
long position = ftell(fp); // 获取当前文件指针的位置
printf("当前位置:%ld\n", position);
fclose(fp);
3. rewind函数
rewind
函数用于将文件指针重置到文件的开头。它的原型是:
void rewind(FILE *stream);
这个函数将stream
指向的文件的文件指针移动到文件的开头。
FILE *fp = fopen("file.txt", "r");
if (fp == NULL) {
printf("无法打开文件\n");
return 1;
}
rewind(fp); // 将文件指针重置到文件的开头
fclose(fp);
使用示例:
fseek(fp, 10, SEEK_SET); // 将文件指针移动到文件的第10个字节
long position = ftell(fp); // 获取当前文件指针的位置
rewind(fp); // 将文件指针重置到文件的开头
五、文件读取结束的判定
1. 文本文件读取结束的判定
我们可以通过检查fgetc
和fgets
函数的返回值来判断是否已经到达文件的末尾。
(1)fgetc函数
fgetc
函数用于从文件中读取一个字符。如果已经到达文件的末尾,或者发生了错误,它将返回EOF
。
FILE *fp = fopen("file.txt", "r");
if (fp == NULL) {
printf("无法打开文件\n");
return 1;
}
int ch;
while ((ch = fgetc(fp)) != EOF) {
putchar(ch);
}
fclose(fp);
在这个例子中,我们使用fgetc
函数在读取文件的过程中判断是否已经到达文件的末尾。当fgetc
函数返回EOF
时,我们就知道已经读取到了文件的末尾,于是停止读取。
(2)fgets函数
fgets
函数用于从文件中读取一个字符串。如果已经到达文件的末尾,或者发生了错误,它将返回NULL
。
FILE *fp = fopen("file.txt", "r");
if (fp == NULL) {
printf("无法打开文件\n");
return 1;
}
char str[100];
while (fgets(str, 100, fp) != NULL) {
printf("%s", str);
}
fclose(fp);
在这个例子中,我们使用fgets
函数在读取文件的过程中判断是否已经到达文件的末尾。当fgets
函数返回NULL
时,我们就知道已经读取到了文件的末尾,于是停止读取。
2. 二进制文件读取结束的判定
二进制文件的读取结束判定与文本文件类似,也可以通过检查读取函数的返回值来进行。在C语言中,我们通常使用fread
函数来读取二进制文件。
fread函数
fread
函数用于从文件中读取数据。它的原型是:
size_t fread(void *ptr, size_t size, size_t count, FILE *stream);
这个函数从stream
指向的文件中读取count
个数据项,每个数据项的大小为size
字节,并将它们存储在ptr
指向的内存区域。函数返回实际读取的数据项数量。如果这个数量小于count
,那么可能已经到达了文件的末尾,或者发生了错误。
以下是一个使用fread
函数的例子:
FILE *fp = fopen("file.bin", "rb");
if (fp == NULL) {
printf("无法打开文件\n");
return 1;
}
int buffer[100];
size_t result = fread(buffer, sizeof(int), 100, fp);
if (result < 100) {
if (feof(fp)) {
printf("到达文件末尾\n");
} else {
printf("发生错误\n");
}
}
fclose(fp);
在这个例子中,我们使用fread
函数在读取文件的过程中判断是否已经到达文件的末尾。当fread
函数返回的数量小于我们期望的数量时,我们就知道可能已经读取到了文件的末尾,于是使用feof
函数进行确认。
附:feof函数和ferror函数
在C语言中,
feof
和ferror
函数用于检查文件读取结束时,判断读取结束的原因为遇到文件结尾结束或遇到错误结束。(1)feof函数
feof
函数用于检查是否是因遇到文件结尾结束。它的原型是:int feof(FILE *stream);
这个函数如果到达了
stream
指向的文件的末尾,就返回非零值。如果没有到达文件的末尾,或者发生了错误,就返回零。(2)ferror函数
ferror
函数用于检查是否是因遇到错误结束。它的原型是:int ferror(FILE *stream);
这个函数如果
stream
指向的文件的最后一个操作出现错误,就返回非零值。如果最后一个操作成功,就返回零。以下是一个使用
feof
和ferror
函数的例子:FILE *fp = fopen("file.txt", "r"); if (fp == NULL) { printf("无法打开文件\n"); return 1; } int ch; while ((ch = fgetc(fp)) != EOF) { putchar(ch); } if (feof(fp)) { printf("到达文件末尾\n"); } else if (ferror(fp)) { printf("发生错误\n"); } fclose(fp);
在这个例子中,我们使用
feof
函数和ferror
函数判断在文件读取结束的时候是因为遇到文件结尾结束还是因为遇到错误结束。
六、文件缓冲区
文件缓冲区是文件读写性能优化的一种手段。在C语言中,所有的文件操作都是通过缓冲区进行的。我们可以使用fflush
函数来清空缓冲区。
fflush(fp); // 清空文件缓冲区
1. 为什么需要文件缓冲区?
文件操作是一种相对较慢的I/O操作。每次对文件进行读写操作,都需要在CPU和磁盘之间进行数据传输,这会消耗大量的时间。为了提高效率,C语言引入了文件缓冲区的概念。
当我们对文件进行读取操作时,C语言会一次性从磁盘中读取大量的数据,并将这些数据存储在文件缓冲区中。然后,我们可以直接从文件缓冲区中读取数据,而不需要每次都去访问磁盘。
同样,当我们对文件进行写入操作时,C语言会先将数据写入到文件缓冲区中,然后在合适的时机一次性将文件缓冲区中的数据写入到磁盘中。
通过这种方式,文件缓冲区可以大大减少CPU和磁盘之间的数据传输次数,从而提高文件读写的效率。
2. 如何使用文件缓冲区?
在C语言中,所有的文件操作都是通过文件缓冲区进行的。我们无需手动管理文件缓冲区,C语言会自动为我们处理。
但是,有时候我们可能需要手动控制文件缓冲区。例如,当我们对文件进行写入操作后,可能需要立即将文件缓冲区中的数据写入到磁盘中。这时,我们可以使用fflush
函数来清空文件缓冲区。
FILE *fp = fopen("file.txt", "w");
if (fp == NULL) {
printf("无法打开文件\n");
return 1;
}
fputs("Hello, World!", fp);
fflush(fp); // 将文件缓冲区中的数据写入到磁盘中
fclose(fp);
在这个例子中,fflush
函数将文件缓冲区中的数据立即写入到磁盘中,而不需要等待C语言自动进行这个操作。