前言:文件操作是C语言的一个内容量比较大的知识点,虽然在实际中运用的并不多,但是学习这个知识点有助于我们更加深入的了解C语言,完善C语言的知识
1.文件的介绍
我们知道电脑内存分为外存和内存,外存就是指电脑磁盘等硬件上的空间,而文件就是磁盘上用于存储数据的载体。在程序设计中我们一般对文件有两种区分方式:
(1)程序文件:包括源文件(比如后缀.c/.cpp/.py),目标文件,可执行文件(.exe)等等
(2) 数据文件:程序运行时读写的文件,程序读取或者输出的对象
文件会有统一的命名格式,这么称之为文件名,主要包含三部分
格式:文件路径:D:\code\test.txt
1.1文本文件和二进制文件
数据在内存中都是以二进制的方式存储的,当我们以记事本打开一些程序的文件的时候显示的可能是一堆乱码,这就是因为里面的二进制数据没有经过转化直接输出出来,这种文件我们就可以称之为二进制文件
而当以ASCII形式在外存中存储的文件(我们可以读懂的文件),我们就可以称之为文本文件
比如我们想存储一个整数1000:

2.文件的打开与关闭
2.1流
C 语言中,程序不能直接操作磁盘文件,而是通过一个抽象层——文件流,来完成数据的输入和输出,程序需要对外部设备获取数据或者传输数据时,就是通过这个流完成的。

2.2标准流
我们平常在c语言中使用scanf或是printf输入或者输出时好像并没有用流的意识,这是因为在c语言中默认打开了三个流,我们称之为标准流
(1)stdin:标准输入流,一般通过键盘来输入
(2)stdout:标准输出流,大多数都是打印在屏幕,在信息输出到标准输出流中
(3)stderr:标准错误流,大多数都是将错误信息输出到屏幕上
这三个流的类型都属于文件指针(FILE*)我们就是通过FILE*来完成各种流的操作
2.3文件指针
文件指针的概念为文件类型指针,每个文件在被使用时都会开辟一个相应的文件信息区,用于存放文件里的相关信息,这些信息存放于一个结构体变量中,类型由系统来声明,在c语言中一般为FILE
所以我们可以像使用指针一样使用它,比如:
FILE* pf;//⽂件指针变量
它指向的是某个文件的信息区,这样我们就可以访问这个文件了,它起到了一个桥梁的作用:

2.4文件的打开与关闭
在操作文件时我们要先打开文件,完成目的后关闭文件否则会占用资源,C语言提供了两个函数来打开和关闭文件
(1)fopen:打开文件 (2)fclose:关闭文件
FILE* pf = fopen("test.txt", "w");//以写的方式打开test.txt文件
fclose(pf);//关闭文件
上面的代码中w表示以写的模式打开文件,我们可以自由的选择打开的方式:
| 打开模式 | 含义 | 文件不存在时 | 文件已存在时 | 权限 |
| "r" (read) | 只读 | 报错 (返回 NULL) | 正常打开 | 只读 |
| "w" (write) | 只写 | 建立新文件 | 清空原文件内容 | 只写 |
| "a" (append) | 追加 | 建立新文件 | 在文件末尾追加内容 | 只写 |
| "r+" | 读写 | 报错 (返回 NULL) | 正常打开 (可覆盖) | 读写 |
| "w+" | 读写 | 建立新文件 | 清空原文件内容 | 读写 |
| "a+" | 读写 | 建立新文件 | 在文件末尾追加内容 | 读写 |
比如我们以写的模式打开这个文件并把它关掉时:
int main()
{
//打开文件
FILE* pf = fopen("test.txt", "w");
if (pf == NULL)
{
perror("fopen");
return 1;
}
//关闭文件
fclose(pf);
pf == NULL;
return 0;
}
3.文件的顺序读写
下面这个表的函数都是顺序读写函数,我们在对一个文件读写时,比如要写入一个字符,当这个字符成功写入时,文件中的光标就会自动指向下一个位置,基于一定顺序读写的函数就称之为顺序读写函数
|
函数 |
函数原型 |
功能介绍 |
返回值 |
|---|---|---|---|
|
fopen |
|
打开指定名称的文件,并关联一个流。 |
成功:指向 |
|
fclose |
|
关闭流,刷新缓冲区。 |
成功: |
|
fgetc |
|
从流中读取一个字符。 |
成功:读取的字符(转为 int);失败/到文件末尾: |
|
fputc |
|
将一个字符写入流中。 |
成功:写入的字符;失败: |
|
fgets |
|
从流中读取一行字符串(最多 n-1 个字符),保留 |
成功:返回 |
|
fputs |
|
将字符串写入流中,不自动追加 |
成功:非负值;失败: |
|
fscanf |
|
从流中读取格式化数据。 |
成功:成功赋值的数据项个数;失败/末尾: |
|
fprintf |
|
将格式化数据输出到流中。 |
成功:写入的字符总数;失败:负数 |
|
fread |
|
从流中读取二进制数据块。 |
成功:实际读取的元素个数;若小于 |
|
fwrite |
|
将二进制数据块写入流中。 |
成功:实际写入的元素个数 |
|
fseek |
|
移动文件指针到指定位置。 |
成功: |
|
ftell |
|
返回当前文件指针相对于起始位置的偏移量。 |
成功:当前位置(字节);失败: |
|
rewind |
|
将文件指针重置回文件的开头。 |
无返回值 |
来举一个使用的具体例子,比如我们想在test.txt中写入小写字母a到z时:
int main()
{
FILE* pf = fopen("test.txt", "w");
if (pf == NULL)
{
perror("fopen");
return 1;
}
char sz = 0;
for (sz = 'a'; sz <= 'z'; sz++)
{
fputc(sz, pf);
}
fclose(pf);
pf == NULL;
return 0;
}
运行这段程序后,再次打开test.txt文件时:

可以看到小写字母被成功的写入了test.txt文件中
当想要读取时也是一样的,这个时候我们可以通过fgetc的返回值来打印出来看看我们是不是成功的读取到了文件里的内容:
int main()
{
FILE* pf = fopen("test.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 1;
}
for (sz = 'a'; sz <= 'z'; sz++)
{
int ret = fgetc(pf);
printf("%c ", ret);
}
fclose(pf);
pf = NULL;
return 0;
}
运行程序:

可以看到我们确实的读取到了文件的内存,并通过返回值成功的在屏幕上打印了文件里的内存
我们还可以以字符串为单位读写数据,比如我想写入两行字符串,可以:
int main()
{
FILE* pf = fopen("test.txt", "w");
if (pf == NULL)
{
perror("fopen");
return 1;
}
fputs("hello world\n", pf);
fputs("hello MIKU", pf);
fclose(pf);
pf = NULL;
return 0;
}
观察运行结果:

4.文件的随机读写
文件的随机读写指的不是光标随机出现,这样未免也太滑稽了,而是通过函数来控制文件的光标顺序来指定位置读写函数:
4.1fseek
这个函数可以根据文件指针的位置和偏移量来定位文件内存的光标
来看看函数的原型:


偏移量就是相对于起始地址前进或者后退了多少位,来看看具体的使用:
这里我们在test.txt文件写入了一串字符abvdefghij ,我们想在读取a之后向后偏移四个位置:
int main()
{
FILE* pf = fopen("test.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 1;
}
int ret = fgetc(pf);
printf("%c\n", ret);
fseek(pf, 4, SEEK_CUR);
ret = fgetc(pf);
printf("%c\n", ret);
fclose(pf);
pf = NULL;
retu
运行结果:

在读取了a之后,光标自动往下移了一位,后面我又通过这个函数让光标移动四个位置让它指向了字符f
4.2rewind与ftell
rewind这个函数可以让光标移动到起始位置,而ftell可以返回相对于起始地址的偏移量还是以一串代码来作为具体的例子:
int main()
{
FILE* pf = fopen("test.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 1;
}
int ret = fgetc(pf);
printf("%c\n", ret);
rewind(pf);
ret = ftell(pf);
printf("%d\n", ret);
fclose(pf);
pf = NULL;
return 0;
}
运行结果:

这个没什么好说的,光标回到了起始位置,偏移量就是0
5.文件读写结束的判定
5.1feor
在判断一个文件读取是否结束时,不能只用feor函数来判断,这个函数的作用是用来判断读取结束的原因是否是遇到文件末尾。因为文件是有可能在读写的过程中发生错误来终止读写,所以我们还要搭配另一个函数ferror:
int main()
{
FILE* pf = fopen("test.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 1;
}
int sz = 0;
while ((sz = fgetc(pf)) != EOF)
{
printf("%c ", sz);
}
printf("\n");
if (feof(pf))
{
printf("遇到末尾,读取正常\n");
}
else if (ferror(pf))
{
printf("读取过程发生错误\n");
}
return 0;
}
但因为遇到文件末尾而接受时返回一个非零值,而当读取过程中失败ferror会返回一个非零值
6.文件缓冲区
程序与文件之间进行数据传输时,要先存放在有系统开辟的文件缓冲区中,只有缓冲区的数据满了之后才会将数据传输到程序或者文件中,缓冲区的大小一般由C语言编译系统决定。

完

被折叠的 条评论
为什么被折叠?



