一、什么是文件?
1.文件的基本概念
文件是存储在计算机或其他数字设备中的信息集合,通常以特定格式组织,用于保存数据、程序或文档。文件通过文件名和扩展名标识,例如 report.docx(文档文件)或 data.xlsx(电子表格文件)。要是没有文件,我们写的程序的数据是存储在电脑的内存中,如果程序退出,内存回收,数据就丢失了,等再次运行程序,就看不到上次程序的数据。
2.文件的常见类型
·文本文件(如 .txt、.csv):存储纯文本或结构化数据。
·文档文件(如 .docx、.pdf):包含格式化文本、图像等,通常由办公软件创建。
·媒体文件(如 .mp3、.mp4):存储音频、视频或图像数据。
·可执行文件(如 .exe、.app):包含程序指令,可直接运行。
在程序设计中,我们可以把上面的文件类型大致分为两类:程序文件、数据文件(从文件功能的角度来分类 的)。
2.1程序文件
程序文件包括源程序文件(后缀为.c),目标文件(windows环境后缀为.obj),可执行程序(windows 环境后缀为.exe)。
2.2数据文件
文件的内容不一定是程序,而是程序运行时读写的数据,比如程序运行需要从中读取数据的文件,或者输出内容的文件。
本章的重点就是数据文件!
3.文件的组成要素
- 文件名:文件的唯一标识符,通常由用户自定义(如 project_plan)。
- 扩展名:后缀(如 .txt、.jpg),用于指示文件类型和关联的应用程序。
- 内容:文件内部存储的实际数据,可以是文本、图像、音频、代码等。
4.文件的管理
- 存储位置:文件保存在硬盘、U盘、云存储等介质中,通过目录(文件夹)分类管理。
- 操作:包括创建、打开、编辑、复制、移动、删除等,可通过操作系统或应用程序完成。
5.二进制文件和文本文件
根据数据的组织形式,数据文件被称为文本文件或者二进制文件。
数据在内存中以二进制的形式存储,如果不加转换的输出到外存的文件中,就是二进制文件。 如果要求在外存上以ASCII码的形式存储,则需要在存储前转换。以ASCII字符的形式存储的文件就是文本文件。
一个数据在文件中是怎么存储的呢? 字符一律以ASCII形式存储,数值型数据既可以用ASCII形式存储,也可以使用二进制形式存储。 如有整数10000,如果以ASCII码的形式输出到磁盘,则磁盘中占用5个字节(每个字符一个字节),而二进制形式输出,则在磁盘上只占4个字节。
两类文件的存储形式如下图所示:
6.文件的打开与关闭
每个被使用的文件都在内存中开辟了一个相应的文件信息区,用来存放文件的相关信息(如文件的名字,文件状态及文件当前的位置等)。这些信息是保存在一个结构体变量中的。该结构体类型是由系统声明的,取名FILE。
6.1 文件指针
文件指针是C语言中用于操作文件的重要工具,它是一个指向上述文件结构体的指针,用于跟踪文件当前读写位置。文件指针的类型为FILE*,通常与文件操作函数如fopen、fclose、fread、fwrite等配合使用。
6.2文件指针的声明与初始化
FILE* pf1;
定义pf1是一个指向FILE类型数据的指针变量。可以使pf指向某个文件的文件信息区(是一个结构体变量)。通过该文件信息区中的信息就能够访问该文件。也就是说,通过文件指针变量能够间接找到与它关联的文件。用图来表达的话就是:
初始化时就可以调用文件操作函数如fopen,如下:
FILE *fp;
fp = fopen("example.txt", "r"); // 以只读模式打开文件
if (fp == NULL) {
perror("Error opening file");
return 1;
}
6.3 文件的打开与关闭
如果我们想要进行读写文件的操作,就要先把文件打开,读写完成后就要把文件关闭。
那要如何操作呢?
规定上,我们可以使用fopen来打开文件,fclose来关闭文件。
1.)fopen
第一个参数表示文件名
第二个参数mode表示文件的打开模式
以下是文件打开模式的综合:
文件打开模式表格
r | 只读模式 | 打开文件 | 报错 (FileNotFoundError) |
w | 写入模式 | 清空文件内容 | 创建新文件 |
a | 追加模式 | 在文件末尾追加内容 | 创建新文件 |
r+ | 读写模式 | 打开文件,可读写 | 报错 (FileNotFoundError) |
w+ | 读写模式 | 清空文件内容,可读写 | 创建新文件 |
a+ | 读写模式 | 在文件末尾追加内容,可读写 | 创建新文件 |
x | 独占创建模式 | 报错 (FileExistsError) | 创建新文件 |
二进制文件模式
在模式后加 b 表示以二进制方式操作文件(如 rb, wb, ab 等)。例如:
- rb:二进制只读模式
- wb:二进制写入模式
- ab:二进制追加模式
其他加b的情况都是二进制文件的操作模式,以下略。
2.)fclose
fclose比较简单,一个参数就是指向要关闭的文件的指针。
7.文件的顺序读写
7.1文件的顺序读写函数:
fopen() | 打开文件 | FILE *fp = fopen("file.txt", "r"); |
fclose() | 关闭文件 | fclose(fp); |
fgetc() | 读取单个字符 | char ch = fgetc(fp); |
fputc() | 写入单个字符 | fputc('A', fp); |
fgets() | 读取字符串(行) | fgets(buffer, 100, fp); |
fputs() | 写入字符串 | fputs("Hello", fp); |
fscanf() | 格式化读取 | fscanf(fp, "%d", &num); |
fprintf() | 格式化写入 | fprintf(fp, "Value: %d", num); |
feof() | 检测文件结束 | while(!feof(fp)){…} |
rewind() | 重置文件指针到开头 | rewind(fp); |
7.2 根据上面的函数以及文件的打开模式,有下列的实例可供参考:
在上述代码我们可以看到,它通过fopen函数用‘w’写入模式打开目标文件,然后使用fputs函数将一串字符写入到了我们的myfile.txt文件中。
然后,我们更改打开模式为‘r'只读模式,通过fgets函数将myfile.txt文件中的字符串读取到字符串数组arr中,并将其打印。
7.3 各种scanf、printf的对比
1.)SCANF
1.1)fcanf
fscanf 是 C 语言中用于从文件中读取格式化输入的函数,类似于 scanf,但操作对象是文件流而非标准输入。函数原型如下:
int fscanf(FILE *stream, const char *format, …);
参数:
- stream:指向文件流的指针(通过 fopen 打开)。
- format:格式化字符串,指定输入数据的解析方式。
- …:可变参数,用于存储读取的数据(需为变量地址)。
基本用法如下:
1.2)sscanf
sscanf 是 C 标准库中的一个函数,用于从字符串中按照指定格式读取数据。其功能类似于 scanf,但输入源是字符串而非标准输入。函数原型如下:
int sscanf(const char *str, const char *format, …);
- str:待解析的输入字符串。
- format:格式控制字符串,指定如何解析输入。
- …:可变参数,用于存储解析结果。
以下是基本用法:
#include <stdio.h>
int main() {
const char *str = "42 3.14";
int num;
float pi;
int result = sscanf(str, "%d %f", &num, &pi);
printf("Parsed %d values: %d, %.2f\\n", result, num, pi);
return 0;
}
2.)PRINTF
2.1)fprintf
fprintf 是 C 语言标准库中的一个函数,用于将格式化输出写入文件流。其功能类似于 printf,但 printf 默认输出到标准输出(通常是屏幕),而 fprintf 可以指定输出到任意文件流(如文件、标准错误等)。函数原型如下:
int fprintf(FILE *stream, const char *format, …);
参数:
- stream:指向文件流的指针,可以是 stdout、stderr 或通过 fopen 打开的文件。
- format:格式字符串,指定输出的格式。
- …:可变参数,根据格式字符串的要求提供相应的变量。
基本用法如下:
2.2)sprintf
sprintf 是一个在多种编程语言中用于格式化字符串的函数, printf 函数用于将格式化字符串输出到标准输出(如屏幕),而 sprintf 则是将格式化字符串写入一个字符数组(字符串)中,而不是直接输出。它返回写入的字符数(不包括结尾的空字符)。函数原型如下:
int sprintf(char *str, const char *format, …);
参数:
- str:目标字符数组,用于存储格式化后的字符串。
- format:格式化字符串,包含普通字符和格式说明符(如 %d、%f)。
- …:可变参数,根据 format 中的格式说明符填入。
基本用法如下:
#include <stdio.h>
int main() {
char buffer[50];
int num = 123;
float pi = 3.14;
// 格式化整数和浮点数
sprintf(buffer, "Number: %d, Pi: %.2f", num, pi);
printf("%s\\n", buffer); // 输出: "Number: 123, Pi: 3.14"
return 0;
}
8.文件的随机读写
有顺序读写,就要有随机读写。随机读写让我们对文件的输入输出有了更灵活的方式。
8.1 fseek
根据文件指针的位置和偏移量来定位文件指针。
int fseek ( FILE * stream, long int offset, int origin );
1.)fseek的第三个参数
fseek函数的第三个参数用于指定偏移量的参考位置,即从文件的哪个位置开始计算偏移量。该参数通常是一个预定义的常量,具体如下:
SEEK_SET
表示从文件开头开始计算偏移量。例如,fseek(fp, 10, SEEK_SET)将文件指针移动到距离文件开头10字节的位置。
SEEK_CUR
表示从当前文件指针位置开始计算偏移量。例如,fseek(fp, 5, SEEK_CUR)将文件指针从当前位置向后移动5字节。
SEEK_END
表示从文件末尾开始计算偏移量。例如,fseek(fp, -3, SEEK_END)将文件指针移动到距离文件末尾3字节的位置。
2.)使用案例:
int main()
{
FILE* pFile;
pFile = fopen("myfile.txt", "w");
if (pFile == NULL)
{
perror("fopen");
return 1;
}
fputs("This is an apple.", pFile);
fseek(pFile, 9, SEEK_SET);
fputs(" sam", pFile);
fclose(pFile);
return 0;
}
最后的结果就是:
我们可以看到fseek函数将指针的位置放到了原字符串的第九位,然后进行修改,就变成了“This is a sample".
8.2 ftell
返回文件指针相对于起始位置的偏移量
long int ftell ( FILE * stream );
我们可以将ftell函数与fseek函数搭配使用,举例如下:
#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);
size = ftell(pFile);
fclose(pFile);
printf("Size of myfile.txt: %ld bytes.\\n", size);
}
return 0;
}
8.3 rewind
rewind函数用于将文件指针重置到文件的开头位置,同时清除文件流的错误标志。它常用于需要重复读取文件内容的场景,无需关闭再重新打开文件。
void rewind(FILE *stream);
典型用法如下:
FILE *file = fopen("example.txt", "r");
if (file == NULL) {
perror("文件打开失败");
return 1;
}
// 第一次读取文件内容
char buffer[100];
fgets(buffer, sizeof(buffer), file);
printf("第一次读取: %s", buffer);
// 重置文件指针到开头
rewind(file);
// 第二次读取相同内容
fgets(buffer, sizeof(buffer), file);
printf("第二次读取: %s", buffer);
fclose(file);
9. 文件读取结束判定
1.feof 的作用
当文件读取结束的时候,判断是读取结束的原因是否是:遇到文件尾结束。因此,我们不能直接使用feof 来判定文件是否读取结束。所以我们还要使用其他办法来判定。
2. 文本文件读取是否结束,判断返回值是否为EOF (fgetc),或NULL(fgets)。
例:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int c; // 注意:int,⾮char,要求处理EOF
FILE* fp = fopen("test.txt", "r");
if(fp==NULL)
{
perror("File opening failed");
return 1;
}
//fgetc 当读取失败的时候或者遇到⽂件结束的时候,都会返回EOF
while ((c = fgetc(fp)) != EOF)
{
putchar(c);
}
//判断是什么原因结束的
if (ferror(fp))
puts("I/O error when reading");
else if (feof(fp))
puts("End of file reached successfully");
fclose(fp);
}
10. 文件缓冲区
ANSIC标准采用“缓冲文件系统”处理的数据文件的,所谓缓冲文件系统是指系统自动地在内存中为 程序中每一个正在使用的文件开辟一块“文件缓冲区”。从内存向磁盘输出数据会先送到内存中的缓 冲区,装满缓冲区后才一起送到磁盘上。如果从磁盘向计算机读入数据,则从磁盘文件中读取数据输入到内存缓冲区(充满缓冲区),然后再从缓冲区逐个地将数据送到程序数据区(程序变量等)。缓冲区的大小根据C编译系统决定的。功能大致如下图所示:
10.1 注意事项
- 程序正常终止或文件关闭时,缓冲区内容会自动刷新。
- 缓冲区未刷新时程序异常退出可能导致数据丢失。
- 默认缓冲模式因系统和文件类型而异。
评论前必须登录!
注册