目录
一、文件的介绍
磁盘上的文件是文件,但是在程序设计中,我们一般谈的文件有两种:程序文件和数据文件(从文件的功能角度来分类的)。
1.程序文件
程序文件包括源程序文件(后缀为.c)、目标文件(Windows环境后缀为.obj)、可执行程序(Windows环境后缀为.exe)。
2.数据文件
文件的内容不一定是程序,而是程序运行时读写的数据,比如程序运行需要从中读取数据的文件,或者输出内容的文件。
根据数据的组织形式,数据文件被称为文本文件或者二进制文件。
数据在内存中以二进制的形式存储,如果不加转换的输出到外存,就是二进制文件。
如果要求在外存上以ASCII码的形式存储,则需要在存储前转换。 以ASCII字符的形式存储的文件就是文本文件。
3.文件名
一个文件要有一个唯一的文件标识,以便于用户识别和引用。
文件名包含三个部分:文件路径+文件名主干+文件后缀
例如:c:\code\test.txt
二、文件操作
在了解文件的相关操作前,我们需要知道流的概念。我们程序的数据需要输出到各种外部设备,也需要从外部设备获取数据,不同的外部设备的输入输出操作各不相同。为了方便程序员对各种设备的操作,我们抽象出了流的概念,我们可以把流想象成流淌着字符的和。
C程序中对文件、画面、键盘等的数据输入输出操作都是通过流操作的。
一般情况下,我们要想向流里写数据,或者从流中读取数据,都是要打开流,然后操作。
草图理解:
然而,在我们实际写代码的过程中,比如在屏幕上打印"hello world",好像并没有说打开什么流,这是因为当C语言程序启动时,默认就打开了三个流:
①stdin——标准输入流,在大多数的环境中从键盘输入,scanf函数就是从标准输入流中读取数据。
②stdout——标准输出流,大多数的环境中输出至显示器界面,printf函数就是将信息输出到标准输出流中。
③stderr——标准错误流,大多数环境中输出到显示器界面。
1.文件的打开和关闭
文件在读写之前应该打开文件,在使用结束后要关闭文件。
在编写程序的时候,在打开文件的同时,都会返回一个FILE*的指针变量指向该文件,也相当于建立了指针和文件的关系。
FILE*是一个文件类型指针,简称文件指针。
每个被使用的文件都在内存中开辟了一个相应的文件信息区,用来存放文件的相关信息(如文件的名字,文件状态及文件当前的位置等)。这些信息是保存在一个结构体变量中的,该结构体类型是由系统声明的,取名FILE。
接下来我们就可以创建文件指针变量FILE* pf,通过文件指针变量能够间接找到与它关联的文件。
ANSIC规定使用fopen函数来打开文件,fclose来关闭文件。
在fopen函数中,第一个参数是文件名,第二个参数mode是文件的打开模式,以下是文件的打开模式:
接下来给一个使用fopen和fclose的例子:
#include <stdio.h>
int main()
{
FILE* pf;
//打开文件
pf = fopen("study.txt", "w");
//文件操作
if (pf != NULL)
{
fputs("hello", pf);
//关闭文件
fclose(pf);
}
return 0;
}
注意:关于fopen中的第一个参数,不一定要是当前路径下的文件,.表示当前路径,..表示上一级路径,例如“./../data.txt”表示当前路径的上一路径下的data.txt文件,这样的是相对路径;绝对路径例子:C:\\Users\\xxx\\data.txt
2.文件的顺序读写
1)fgetc和fputc
①fgetc
fgetc只有一个参数,需要传入一个 FILE*类型的指针。fgetc是用于从指定的文件流中读取下一个字符的函数。
我们先在当前路径下建立一个study.txt文件,并在里面写入“hello”,然后再使用fgetc得到第一个字符,并将其打印在屏幕上。
②fputc
foutc是用于将单个字符写入指定的文件流中的函数,它含有两个参数,第一个参数是要写入的字符,第二个参数是FILE*类型的指针。
根据上述例子,可以发现,study.txt文件中只有a了,事实上,当以写的方式打开文件时,其原有内容会清空。
2)fgets和fputs
①fgets
fgets是一个用于从文件或输入流中读取字符串的函数,它含有三个参数,其中第一个参数str用于存储从文件或输入流中读取的字符串,而第二个参数num是字符个数。
示例:我们依旧将study.txt的内容改成“hello”,然后进行读取
通过上述例子发现,我们想的是能够读取三个字符,那应该输出“hel”,但实际上只输出了两个字符,接下来我们给str中各元素赋值,然后调试,看str中的变化
调试发现:fgets实际上是读取num-1个字符,然后在后边补一个'\0'
②fputs
fputs就比较简单了,它有两个参数,第一个参数str是要写入的字符串。
示例:
3)fscanf和fprintf
①fscanf
对比fscanf和scanf,我们发现,fscanf相对scanf来说只多了一个参数,所以它们的用法也是非常相似的,scanf怎么用,fscanf就怎么用。scanf是从stdin即标准输入流中读取,而 fscanf是从stream指向的文件中读取。
示例:我们依旧将study.txt中内容改成“hello”,然后用fscanf读取。
②fprintf
同样地,fprintf与printf也非常相似,只是多了一个参数,用法都差不多。
示例:我们依旧将study.txt中内容改成“hello”,然后在里面写入。
4)fread和fwrite
fread和fwrite分别是以二进制的形式读文件和写文件的。
fread有四个参数,第一个参数 ptr指向要存储读取数据的内存块的指针,第二个参数size是每个数据项的大小(以字节为单位),第三个参数count是要读取的数据项的数量,第四个参数stream指向要读取的文件。
fwrite参数与fread参数类似,只是第一个参数ptr是一个指向要写入文件的数据块的指针 。
示例:建立一个结构体,然后将其以二进制形式写入和读取
可以看到,用记事本打开是看不懂以二进制形式写入的东西的,但是通过用fread读,然后打印,我们可以看到是正确的。
3.文件的随机读写
1)fseek
fseek用于改变光标位置,它有三个参数,其中第二个参数offset表示的是位置的偏移量,第三个参数origin表示的是偏移的起始位置。
origin可以是以下三个值之一:
SEEK_SET
:文件的开头。SEEK_CUR
:当前文件指针的位置。SEEK_END
:文件的结尾。
注意:fseek函数主要用于二进制文件,因为文本文件可能会因为平台差异(如换行符的不同表示)而出现问题。在使用fseek时,应确保你正在处理的是二进制文件或你了解正在处理的文本文件的具体行为。
示例:我们先在study.txt中写入“abcdef”,然后用fgetc读取并打印,打印过程中使用fseek改变光标位置。
可以看到,如果不改变光标位置,直接打印三次,结果应该是“abc”,在第三次打印时,我们把光标位置从当前位置后移了3,所以第三次打印的是f。
注意,后移,第二个参数为正数,前移则是负数。
2)ftell
ftell用于获取当前文件位置指针的偏移量。它返回的是从文件开头到当前文件指针位置的字节数。
仍旧以上面使用的fseek的例子来验证:
3)rewind
rewind用于将文件的位置指针重新设置到文件的开头。这意味着,当你调用rewind函数后,下一次的读取或写入操作将从文件的第一个字节开始。与ftell一样,它只有一个参数,用于找到对应的文件。
4.文件读取结束的判定
区分feof和ferror,feof是在文件读取结束后,判断是否是因为遇到文件末尾而结束,ferror是在文件读取结束后,判断是否是因为遇到错误而结束。
注意:
①文本文件读取是否结束,判断返回值是否为EOF(fgetc),或者NULL(fgets)
②二进制文件的读取是否结束,判断返回值是否小于实际要读的个数(例如fread)。