目录
1、文件操作概述
每个被使⽤的文件都在内存中开辟了⼀个相应的⽂件信息区,⽤来存放⽂件的相关信息(如⽂件的名字,文件状态及⽂件当前的位置等)。这些信息是保存在⼀个结构体变量中的。该结构体类型是由系统声明的,取名FILE。每当打开⼀个⽂件的时候,系统会根据⽂件的情况⾃动创建⼀个FILE结构的变量,并填充其中的信息,使⽤者不必关心细节。
⼀般通过⼀个FILE的指针来维护这个FILE结构的变量,这样使⽤起来更加⽅便,定义如下:
FILE* pf;//⽂件指针变量
定义pf是⼀个指向FILE类型数据的指针变量。可以使pf指向某个⽂件的⽂件信息区(是⼀个结构体变量)。通过该⽂件信息区中的信息就能够访问该⽂件。也就是说,通过文件指针变量能够间接找到与它关联的文件。
2、文件的打开与关闭
文件在读写之前应该先打开文件,在使用结束之后应该关闭文件。
在编写程序的时候,在打开文件的同时,都会返回⼀个 FILE* 的指针变量指向该文件,也相当于建立了指针和文件的关系。fopen 函数来打开⽂件, fclose 函数来关闭⽂件。
FILE *fopen(const char *filename, const char *mode);
int fclose ( FILE * stream );
其中 fileame 是要打开的文件名;mode表示文件的打开模式,如下:
⽂件使⽤⽅式 | 含义 | 如果指定⽂件不存在 |
“r”(只读) | 为了输⼊数据,打开⼀个已经存在的⽂本⽂件 | 出错 |
“w”(只写) | 为了输出数据,打开⼀个⽂本⽂件 | 建⽴⼀个新的⽂件 |
“a”(追加) | 向⽂本⽂件尾添加数据 | 建⽴⼀个新的⽂件 |
“rb”(只读) | 为了输⼊数据,打开⼀个⼆进制⽂件 | 出错 |
“wb”(只写) | 为了输出数据,打开⼀个⼆进制⽂件 | 建⽴⼀个新的⽂件 |
“ab”(追加) | 向⼀个⼆进制⽂件尾添加数据 | 建⽴⼀个新的⽂件 |
“r+”(读写) | 为了读和写,打开⼀个⽂本⽂件 | 出错 |
“w+”(读写) | 为了读和写,建议⼀个新的⽂件 | 建⽴⼀个新的⽂件 |
“a+”(读写) | 打开⼀个⽂件,在⽂件尾进⾏读写 | 建⽴⼀个新的⽂件 |
“rb+”(读写) | 为了读和写打开⼀个⼆进制⽂件 | 出错 |
“wb+”(读写) | 为了读和写,新建⼀个新的⼆进制⽂件 | 建⽴⼀个新的⽂件 |
“ab+”(读写) | 打开⼀个⼆进制⽂件,在⽂件尾进⾏读和写 | 建⽴⼀个新的⽂件 |
实例代码如下:
#include <stdio.h>
int main() {
FILE *fp; // 声明一个文件指针
fp = fopen("C:\\Users\\dell\\Desktop\\example.txt", "r"); // 打开文件,并将返回的文件指针赋值给fp
if (fp == NULL) {
perror("Error opening file");
return -1;
}
// ... 文件操作代码 ...
fclose(fp); // 关闭文件,并释放文件指针
return 0;
}
3、文件的顺序读取与写入
函数名 | 功能 | 适⽤于 |
fgetc | 字符输⼊函数 | 所有输⼊流 |
fputc | 字符输出函数 | 所有输出流 |
fgets | ⽂本⾏输⼊函数 | 所有输⼊流 |
fputs | ⽂本⾏输出函数 | 所有输出流 |
fscanf | 格式化输⼊函数 | 所有输⼊流 |
fprintf | 格式化输出函数 | 所有输出流 |
fread | ⼆进制输⼊ | ⽂件 |
fwrite | ⼆进制输出 | ⽂件 |
上⾯说的适用于所有输入流⼀般指适用于标准输入流和其他输入流(如文件输⼊流);所有输出流⼀般指适用于标准输出流和其他输出流(如文件输出流)。
3.1 fputs、fgets函数
fputs 函数是一个用于向文件写入字符串的标准库函数。将指定的字符串(不包括空字符 '\0'
)写入到指定的输出流中(一般为文件)。如果成功,fputs
函数返回非负值;如果发生错误,则返回 EOF
(通常定义为 -1)。
int fputs(const char *s, FILE *stream);
s
是指向以空字符'\0'
结尾的字符串的指针。注意,fputs
不会写入字符串的终止空字符。stream
是指向FILE
对象的指针,该对象标识了要写入的输出流。在文件操作中,这通常是一个通过fopen
函数打开的文件指针。
fgets函数用于从指定的文件流中读取一行数据,并存储到字符串中。这个函数会读取直到遇到换行符(\n
)、文件结束符(EOF)或已经读取了指定数量的字符(不包括最后的空字符 \0
,该函数会自动在字符串末尾添加这个空字符以标记字符串的结束)为止的字符。如果成功会返回指向 str
的指针;如果到达文件末尾(EOF)或发生错误,它会返回 NULL
。但是,请注意,如果读取的最后一行没有换行符,并且读取的字符数小于 n-1
,则 fgets
仍会成功返回,并且字符串会以空字符 \0
结尾,但不会自动添加换行符。
char *fgets(char *str, int n, FILE *stream);
str
是一个指向字符数组的指针,用于存储读取的字符串。n
是要读取的最大字符数(包括最后的空字符\0
)。因此,实际上最多可以读取n-1
个字符加上一个空字符\0
。stream
是一个指向FILE
对象的指针,该对象标识了要从中读取数据的输入流。
代码示例如下:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main() {
FILE *fp;
// 打开文件以写入
fp = fopen("C:\\Users\\dell\\Desktop\\example.txt", "w");
if (fp == NULL) {
perror("Error opening file");
return(-1);
}
// 写入数据
fputs(fp, "Hello, World!\n");
// 关闭文件
fclose(fp);
// 重新打开文件以读取
fp = fopen("C:\\Users\\dell\\Desktop\\example.txt", "r");
if (fp == NULL) {
perror("Error opening file");
return(-1);
}
// 读取数据
char buff[255];
if (fgets(buff, 255, fp) != NULL)
printf("%s", buff);
// 关闭文件
fclose(fp);
return 0;
}
3.2 fscanf、fprintf 函数
fscanf
函数用于从指定的文件流中读取数据,并根据提供的格式字符串解析这些数据,其返回值为成功读取的项目数,如果到达文件末尾或发生读取错误,则可能返回 EOF。
int fscanf(FILE *stream, const char *format, ...);
stream
是指向FILE
对象的指针,该对象标识了要从中读取数据的文件流。format
是格式字符串,指定了后续参数的类型和如何从输入中解析它们。...
表示可变数量的附加参数,这些参数指向用于存储从文件中读取的数据的变量。
fprintf
函数用于向指定的文件流写入数据,其工作方式类似于 printf
,但它是向文件写入而不是标准输出。 其返回值为成功写入的字符数(不包括末尾的空字符),如果发生错误,则返回负值。
int fprintf(FILE *stream, const char *format, ...);
stream
是指向FILE
对象的指针,该对象标识了要写入数据的文件流。format
是格式字符串,指定了后续参数如何被格式化为字符串并写入文件。...
表示可变数量的附加参数,这些参数是要写入文件的数据。
代码示例如下:
#include <stdio.h>
int main() {
FILE *inputFile = fopen("C:\\Users\\dell\\Desktop\\example.txt", "r");
if (inputFile == NULL) {
perror("Error opening input file");
return -1;
}
FILE *outputFile = fopen("C:\\Users\\dell\\Desktop\\output.txt", "w");
if (outputFile == NULL) {
fclose(inputFile); // 不要忘记关闭已打开的文件
perror("Error opening output file");
return -1;
}
int age;
float height;
char name[50]; // 假设名字不会超过49个字符加上一个空字符
// 从输入文件读取数据
while (fscanf(inputFile, "%s %d %f", name, &age, &height) == 3) {
// 处理数据(在这个例子中我们只是简单地回显)
// 将处理后的数据(或原样)写入输出文件
fprintf(outputFile, "Name: %s, Age: %d, Height: %.2f\n", name, age, height);
}
// 关闭文件
fclose(inputFile);
fclose(outputFile);
return 0;
}
注:这个例子中使用了%s
来读取字符串(即名字)。然而,使用%s
与fscanf
一起读取字符串时应当格外小心,因为它不会检查目标缓冲区的边界,可能会导致缓冲区溢出。在实际应用中,你可能想要使用fgets
和sscanf
(或适当的字符串处理函数)的组合来更安全地读取字符串。
此外,假设input.txt
文件包含以下格式的数据:
John 30 1.75
Jane 25 1.65
Doe 40 1.80
执行上述程序后,output.txt
文件将包含相同的数据,但每行前面都添加了"Name: ", "Age: ", 和 "Height: "的标记,并且数据项之间以逗号分隔。
4、文件的随机读取与写入
文件指针的偏移(offset)是指文件指针当前位置与文件开头之间的字节数。通过改变文件指针的偏移量,可以实现对文件的随机访问。
4.1 fseek函数
fseek 函数根据文件指针的位置和偏移量来定位文件指针,如果 fseek
函数成功执行,它会返回 0
。如果发生错误(例如,指定的位置超出了文件的实际大小,或者底层系统不支持文件定位操作),它会返回非零值,并设置 errno
以指示错误类型。
int fseek(FILE *stream, long offset, int whence);
stream
是指向FILE
对象的指针,该对象标识了要操作的文件。offset
是相对于whence
指定的位置的偏移量,以字节为单位。偏移量可以是正数或负数。whence
是一个整数,指定了offset
的起始位置。它必须是以下三个常量之一:SEEK_SET
:文件的开头。SEEK_CUR
:文件指针的当前位置。SEEK_END
:文件的末尾。
以下是一个使用 fseek
函数的示例,该示例将文件指针移动到文件的开头,然后向前移动一定字节数,并读取该位置之后的数据:
#include <stdio.h>
#include <stdlib.h>
int main() {
FILE *fp = fopen("example.txt", "r");
if (fp == NULL) {
perror("Error opening file");
return EXIT_FAILURE;
}
// 将文件指针移动到文件的开头
if (fseek(fp, 0, SEEK_SET) != 0) {
perror("Error seeking to the beginning of the file");
fclose(fp);
return EXIT_FAILURE;
}
// 假设我们要跳过前10个字节
if (fseek(fp, 10, SEEK_SET) != 0) {
perror("Error seeking to 10 bytes from the beginning of the file");
fclose(fp);
return EXIT_FAILURE;
}
// 从当前位置开始读取数据
char buffer[100];
if (fgets(buffer, sizeof(buffer), fp) != NULL) {
// 处理读取的数据...
printf("%s", buffer);
}
// 关闭文件
fclose(fp);
return EXIT_SUCCESS;
}
4.2 ftell函数
ftell函数用于获取文件指针的当前位置,即相对于文件开头的偏移量(以字节为单位)。
#include <stdio.h>
int main()
{
FILE* pFile;
long size;
pFile = fopen("myfile.txt", "rb");
if (pFile == NULL)
perror("Error opening file");
else
{
fseek(pFile, 0, SEEK_END); // non-portable
size = ftell(pFile);
fclose(pFile);
printf("Size of myfile.txt: %ld bytes.\n", size);
}
return 0;
}
4.3 rewind 函数
让⽂件指针的位置回到⽂件的起始位置。
#include <stdio.h>
int main() {
FILE *fp;
char buffer[100];
// 打开文件
fp = fopen("example.txt", "r");
if (fp == NULL) {
perror("Error opening file");
return(-1);
}
// 第一次读取文件
if (fgets(buffer, 100, fp) != NULL) {
printf("First time reading: %s", buffer);
}
// 使用 rewind 重置文件位置指示器
rewind(fp);
// 第二次读取文件(从头开始)
if (fgets(buffer, 100, fp) != NULL) {
printf("Second time reading: %s", buffer);
}
// 关闭文件
fclose(fp);
return 0;
}
注意事项:
- 使用
rewind
函数之前确保文件已经成功打开,并且是以读取模式(如 "r" 或 "r+")打开的。 - 如果文件是以写入模式("w" 或 "w+")打开的,并且文件已经存在,那么
rewind
函数会将位置指示器重置到文件的开头,但此时文件的内容可能已经被清空(取决于具体的实现和文件模式)。 - 在使用完文件后,记得使用
fclose
函数关闭文件,以释放相关资源。
5、文件读取结束的判定
在文件读取过程中,不能⽤ feof 函数的返回值直接来判断⽂件的是否结束。 feof 的作用是:当文件读取结束的时候,判断读取结束的原因是否是:遇到文件尾结束。
1. ⽂本⽂件读取是否结束,判断返回值是否为 EOF ( fgetc ),或者 NULL ( fgets )
例如:
• fgetc 判断是否为 EOF .
• fgets 判断返回值是否为 NULL .
2. ⼆进制⽂件的读取结束判断,判断返回值是否⼩于实际要读的个数。
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int c; // 注意:int,⾮char,要求处理EOF
FILE* fp = fopen("test.txt", "r");
if (!fp) {
perror("File opening failed");
return EXIT_FAILURE;
}
//fgetc 当读取失败的时候或者遇到⽂件结束的时候,都会返回EOF
while ((c = fgetc(fp)) != EOF) // 标准C I/O读取⽂件循环
{
putchar(c);
}
//判断是什么原因结束的
if (ferror(fp))
puts("I/O error when reading");
else if (feof(fp))
puts("End of file reached successfully");
fclose(fp);
}
标签:fp,文件,读取,函数,FILE,操作,指针,语言
From: https://blog.csdn.net/2301_81723939/article/details/142026240