所谓“文件”是指一组相关数据的有序集合,该数据的集合的名字就是文件名。文件可以分为很多类,如源程序文件、目标文件、可执行文件、库文件等等。
文件通常是存放在外部介质上的(例如磁盘等),在使用时才会被调入内存中并执行。从用户的角度来看文件可以分为普通文件和设备文件。
普通文件是指存放在磁盘或者其它外部介质上的一个有序的集合,可以是源文件、目标文件、可执行程序等;也可以是一组待输入处理的原始数据,或是一组输出的结果。对于源文件、目标文件、可执行程序可以称作程序文件,而输入输出数据可以称作数据文件。
设备文件是指与主机相联的各种外部设备,如显示器、打印机、键盘等。在操作系统中,把外部设备也看作是一个文件来进行管理,把他们的输入输出等同于对磁盘文件的读写。
从文件编码的形式来看,文件可以分为ASCII码文件和二进制码文件。ASCII码文件也称作为文本文件,这种文件在磁盘中存放时每个字符对应一个字节,用于存放相应的ASCII码。
二进制文件是以二进制编码的方式来编写文件的。二进制文件虽然可以显示在屏幕上,但是却不能读懂。
文件的指针
在C语言中用一个指针变量指向一个文件,那么这个指针称为文件指针。另外,我们通过文件指针就可以对所指的文件进行各种操作。
一般形式为:
FILE* 文件变量标识符
解释:FILE应该是大写的,它实际上是由系统定义的一个结构体,该结构体包含了文件名、文件状态和文件当前位置等信息,因此在编写程序的时候我们不需要过于关心FILE结构的细节部分。
// 案例1:
FILE *fp;
/*说明:
fp表示FILE结构的指针变量,通过fp可以找到存放某一个文件信息的结构变量,
然后按照结构体提供的信息找到该文件,并对文件进行操作。*/
文件的打开和关闭
文件在进行读写操作之前,要打开文件,当使用完后应关闭文件。打开文件就是建立文件的各种有关信息,并使文件指针指向文件,以便进行其他操作。而关闭文件就是切断文件指针和文件之间的关系,换而言之,就是禁止利用指针操作文件。
在C语言中,文件操作都是由库函数完成的。如fopen 和 fclose。
1. 文件的打开 fopen()
fopen() 函数是用来打开一个文件,其一般的调用形式为:
文件指针名 = fopen(文件名,使用文件的方式);
注释:
- 文件指针名:必须被说明为FILE类型的指针变量;
- 文件名:被打开文件的文件名;
- 使用文件的方式:文件的类型和操作要求
// 案例2:
FILE* fp;
fp = fopen("text","r");
/*说明:
在当前目录下打开文件text,只允许“读”的操作,并且让fp指针指向该文件*/
使用文件的方式有12种,如下所示:
|使用文件方 | 意义 |
|----------|------------------------------:|
|“rt” | 只读打开一个文本,只允许读|
|“wt” | 只写打开或者建立一个文件,只允许写数据|
|“at” | 追加打开一个文件,并在文件末尾写数据|
|“rb” | 只读打开一个二进制文件,只允许读|
|“wt” | 只写打开或者建立一个而进制文件,只允许写|
|“ab” | 追加打开一个二进制文件,并在文件末尾写数据|
|“rt+” | 读写打开一个文件,允许读和写 |
| “wt+” | 读写打开或建立一个文件,允许读写|
|“at+” | 读写打开一个文件,允许读,或在文件末尾追加数据|
|“rb+” | 读写打开一个二进制文件,允许读写|
|“wb+” | 读写打开或者建立一个二进制文件,允许读写 |
|“ab+” | 读写打开一个二进制文件,允许读,或在文件末追加数据|
说明:
由r、w、a、t、b、+六个字符拼成,个字符的意义:
- r(read):读
- w(write):写
- a(append):追加
- t(text):文本文件,可省略不写
- b(banary):二进制文件
6.+:读和写
// 案例3:
FILE* fp;
fd = fopen("./text","r");
if (NULL == fd)
{
printf("open error\n");
exit(1);
}
// 用读“r”的方式打开文件“./text”;若文件打开失败时,fp为空,输出open error。
2. 文件的关闭
fclose()函数是指当文件使用完后,需要关闭文件。其一般形式为:
fclose(文件指针);
// 案例4:
fclose(fp);
// 说明:正常关闭文件时,fclose()函数的返回值是0。若返回一个非零的值,则表示关闭文件时发生错误
文件的读写
文件的读写是有多重方式的,它可以一个字节一个字节的读或写,也可以是一串一串的读或写。文件的读写可以分为下面四类,且它们的头文件均是<stdio.h>。
1. 字符读写函数fgetc 和 fputc
字符的读写函数是以字节为单位读写函数。每一次可以从文件读出或向文件内写入一个字符。
读字符函数fgetc() 的一般形式:
字符变量 = fgetc(文件指针);
说明:将文件中的一个字符读取出来,然后存放在字符变量中。
写字符函数fputs() 的一般形式:
fputs(字符量,文件指针);
说明:将字符量中的字符,存放在想对应的文件中。
// 案例5:
#include<stdio.h>
int main()
{
FILE* fp = fopen("./text","r");
if (NULL == fp)
{
printf("cann't open the file\n");
exit(1);
}
char ch;
// 读取文件中的字符,一个一个的读取。
ch = fgetc(fp);
while (EOF != ch) // EOF表示读到文件的末尾
{
printf("%c\n",ch);
ch = fgetc(fp);
}
close(fp);
// 向文件中写入数据
fp = fopen("./text1","w");
if (NULL == fp)
{
printf("cann't open the file\n");
exit(1);
}
printf("input a character:");
scanf("%c",&ch);
fputc(ch,fp); // 将ch变量中的字符存放到文件“./text1”中。
return 0;
}
注意:在网文件中写数据的时候,上的那种写法是,直接覆盖文件中的数据,而不是在文件的末尾追加数据。若想不改变文件原来的数据,只想在文件的末尾追加数据,那么需要将文件的打开方式“w”更改为“a”,这样我们就可以在原数据的基础上追加数据。
2. 字符串读写函数fgets 和 fputs
fgets() 的一般形式:
fgets(字符数组名,n,文件指针);
说明:n是一个正整数,表示从文件中读取的字符串不超过n-1个字符,在读入最后一个字符后加上结束符'\0'。
fputs()的一般形式:
fputs(字符数组名,文件指针);
说明:字符数组名,首先其中必须是一个已被初始化的字符数组,即里面有值;也可以用一个字符串常量(或指针变量)代替字符数组名。
//案例6:
#include<stdio.h>
int main()
{
char ch[20];
FILE *fp = fopen("./text","rw");
if (NULL == fp)
{
printf("open error\n");
exit(1);
}
printf("input what you want to say:");
scanf("%s",ch);
//将字符串ch中的值写在文件text中。
fputs(ch,fp);
char ch2[30];
// 从文件中读取数据,并由ch2字符串变量接收。
fgets(ch2,sizeof(ch2)-1,fp);
printf("ch2:%s\n",ch2);
return 0;
}
3. 数据块读写函数fread 和fwrite
C语言中不仅提供了字符、字符串的读写方式,同时也提供了数据块的读写方式。它们可用来读写一组数据,如:一个数组元素,一个结构变量的值等。
fread()的一般形式:
fread(buffer,size,count,fp);
fwrite()的一般形式:
fwrite(buffer,size,count,fp);
说明:
- buffer 是一个指针,在fread函数中表示存放数据的首地址。在fwrite函数中表示输出数据的首地址;
- size 表示数据块的字节数;
- count 表示要读写的数据块的块数
- fp 表示文件的指针
//案例7:
#include<stdio.h>
struct student
{
int id;
char name[20];
float score;
};
int main()
{
struct student boy[2];
int i = 0;
for (;i < 2; i++)
{
printf("input the student's infomation(id, name, score):");
scanf("%d%s%f",&boy[i]->id,boy[i]->name,&boy[i]->score);
}
FILE* fp;
fp = fopen("./student.text","w+");
if (NULL == fp)
{
printf("open file error\n");
exit(1);
}
fwrite(boy,sizeof(struct student),2,fp);
rewind(fp);
struct student stu[2];
fread(stu,sizeof(struct student),2,fp);
printf("id\tname\tscore\n");
for (i = 0; i < 2; i++)
{
printf("%d\t%s\t%f\n",stu[i]->id,stu[i]->name,stu[i]->score);
}
return 0;
}
4. 格式化读写函数fscanf 和 fprintf
fscanf、fprintf和scanf、printf的区别:
fscanf和fprintf函数的读写不是对键盘和显示器的,而是对磁盘文件;不过它们都是格式化读写函数。
fscanf 的一般形式:
fscanf(文件指针, 格式字符串, 输入列表);
fprintf 的一般形式:
fprintf(文件指针, 格式字符串, 输出列表);
//案例8:
#include<stdio.h>
int main()
{
char caBuf[3][32] = {'\0'};
int i = 0;
for (; i < 3; i++)
{
printf("input :");
scanf("%s", caBuf[i]);
}
FILE *fp;
fp = fopen("./MyInput", "wb+");
if (NULL == fp)
{
printf("open file error\n");
exit(1);
}
for (i=0; i < 3; i++)
{
fprintf(fp, "%s", caBuf[i]);
}
rewind(fp);
char sMsg[32] = {'\0'};
for(i=0; i < 3; i++)
{
fscanf(fp, "%s", sMsg);
printf("sMsg: %s\n", sMsg);
}
fclose(fp);
return 0;
}
文件定位
有的时候我们在读写文件时,不想从开头或者末尾开始读或者写;想读取中间的内容,或者是想将一段数据插入到文件中间,我们该怎么做?这时候,我们就需要引入新的知识,文件的定位。
文件定位函数
在文件定位中,主要有两个函数:rewind()和fseek().
rewind() 在前面已经使用,并进行一定的说明,不过在这里还是要重申一下,其一般形式为:
rewind(文件指针);
功能:文件内部的文件指针转移到文件首部。
fseek() 的一般形式:
fseek(文件指针, 位移量, 起始点);
参数说明:
文件指针:被指向移动的文件;
位移量:表示移动的字节数;
起始点:表示从何处开始计算位移量,规定有三种:文件首部(SEEK_SET,1),当前位置(SEEK_CUR,0)和文件尾部(SEEK_END, 2)。
// 案例10:
fseek(fp, 100, 0);
//表示:把文件位置指针移到距离首部100个字节处。
注意事项:fseek()一般用于二进制文件。在文本文件中由于要进行转换,故往往计算位置会出现错误